09_01_SpringBoot

Spring Boot

1. SpringBoot基本应用

1.1 约定优于配置

Build Anything with Spring Boot:Spring Boot is the starting point for building all Spring-based applications. Spring Boot is designed to get you up and running as quickly as possible, with minimal upfront configuration of Spring.

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

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

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

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

1.2 SpringBoot概念

1.2.1 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整合包、数据库驱动,引入依赖的数量繁 多、容易存在版本冲突。

1.2.2 Spring Boot解决上述spring问题

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

起步依赖

起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依 赖,这些东西加在一起即支持某项功能。

简单的说,起步依赖就是将具备某种功能的依赖坐标打包到一起,并提供一些默认的功能。

自动配置

springboot的自动配置,指的是springboot,会自动将一些配置类的bean注册进ioc容器,我们可以需 要的地方使用@autowired或者@resource等注解来使用它。

只需要引我们想用功能的包,相关的配置我们完全不用管,springboot会自动注入这些配置bean,我们直接使用这些bean即可

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

1.3 Spring Boot入门案例

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

(1)依赖管理
<!--
所用的springBoot项目都会直接或者间接的继承spring-boot-starter-parent
1.指定项目的编码格式为UTF-8
2.指定JDK版本为1.8
3.对项目依赖的版本进行管理,当前项目再引入其他常用的依赖时就不需要再指定版本号,避免版本冲突的问题
4.默认的资源过滤和插件管理
-->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.4.RELEASE</version>
</parent>

<dependencies>
    <!--引入Spring Web及Spring MVC相关的依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

<!--可以将project打包为一个可以执行的jar-->
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
(2)启动类
/**
* SpringBoot的启动类通常放在二级包中,比如:com.lagou.SpringBootDemo1Application
* 因为SpringBoot项目在做包扫描,会扫描启动类所在的包及其子包下的所有内容。
*/
//标识当前类为SpringBoot项目的启动类
@SpringBootApplication
public class SpringBootDemo1Application {
    public static void main(String[] args) {
        //样板代码
        SpringApplication.run(SpringBootDemo1Application.class, args);
    }
}
(3)Controller
package com.lagou.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/hello")
public class HelloController {
    
    @RequestMapping("/boot")
    public String helloBoot(){
        return "Hello Spring Boot";
    }
    
}

1.4 SpringBoot 快速构建

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

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

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

在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

(2) 创建一个用于Web访问的Controller
  • 创建一个请求处理控制类com.lagou.controller.HelloController, 并编写一个请求处理方法

  • 注意:将项目启动类SpringBootDemoApplication移动到com.lagou包下

@RestController // 该注解为组合注解,等同于Spring中@Controller+@ResponseBody注解
public class DemoController {
    @RequestMapping("/demo")
    public String demo(){
        return "hello spring Boot";
    }
}
(3) 运行项目

运行主程序启动类SpringbootDemoApplication,项目启动成功后,在控制台上会发现Spring Boot项目默认启动的端口号为8080,此时,可以在浏览器上访问“http://localhost:8080/hello”。页面输出的内容是“hello Spring Boot”,至此,构建Spring Boot项目就完成了

1.5 单元测试与热部署

开发中,每当完成一个功能接口或业务方法的编写后,通常都会借助单元测试验证该功能是否正确。Spring Boot对项目的单元测试提供了很好的支持,在使用时,需要提前在项目的pom.xml文件中添 加spring-boot-starter-test测试依赖启动器,可以通过相关注解实现单元测试

1.5.1 单元测试
1.添加spring-boot-starter-test测试依赖启动器
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>

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

2.编写单元测试类和测试方法

使用Spring Initializr方式搭建的Spring Boot项目,会在src.test.java测试目录下自动创建与项目主程序 启动类对应的单元测试类

package com.lagou;

import com.lagou.controller.HelloController;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit4.SpringRunner;

/**
* SpringJUnit4ClassRunner.class:Spring运行环境
* JUnit4.class:JUnit运行环境
* SpringRunner.class:Spring Boot运行环境
*/
@RunWith(SpringRunner.class) //@RunWith:运行器
@SpringBootTest //标记为当前类为SpringBoot测试类,加载项目的ApplicationContext上下文环境
class Springbootdemo2ApplicationTests {
    /**
* 需求:调用HelloController的hello方法
*/
    @Autowired
    private HelloController helloController;
    
    @Test
    void contextLoads() {
        String result = helloController.hello();
        System.out.println(result);
    }
}

1.5.2 热部署

Spring Boot框 架专门提供了进行热部署的依赖启动器,用于进行项目热部署,而无需手动重启项目 。

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

1.添加spring-boot-devtools热部署依赖启动器
<!-- 引入热部署依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-devtools</artifactId>
</dependency>
2. IDEA工具热部署设置
开启Idea的自动编译

File->Settings,选择Build下的Compiler选项,在右侧勾选“Build project automatically”选项将项目设置为自动编译,保存设置

开启Idea的在项目运行中自动编译的功能

在项目任意页面中使用组合快捷键“Ctrl+Shift+Alt+/”打开Maintenance选项框,选中并打开 Registry页面,列表中找到“compiler.automake.allow.when.app.running”,将该选项后的Value值勾选,用于指 定IDEA工具在程序运行过程中自动编译,最后单击【Close】按钮完成设置

3.热部署效果测试

打开http://localhost:8080/hello查看效果;接下来,在不关闭当前项目的情况下,将DemoController 类中的请求处理方法hello()的返回值修改,查看控制台信息会发现项目能够自动构 建和编译,说明项目热部署生效;同时,刷新浏览器后输出内容随之修改,说明项目热部署配置成功

1.6 全局配置文件

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

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

  • application.properties
  • application.yaml或application.yml
1.6.1 application.properties配置文件

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

#修改tomcat的版本号
server.port=8888
#定义数据库的连接信息 JdbcTemplate
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/lagou
spring.datasource.username=root
spring.datasource.password=wu7787879

接下来,通过一个案例对Spring Boot项目中application.properties配置文件的具体使用进行讲解

(1)将配置属性注入到实体类
public class Pet {
    private String type;
    private String name;
}

演示将application.properties配置文件中的自定义配置属性(下一步编写)注入到 Person实体类的对应属性中

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
    private int id; //id
    private String name; //名称
    private List hobby; //爱好
    private String[] family; //家庭成员
    private Map map;
    private Pet pet; //宠物
}

注解:

  • @ConfigurationProperties(prefix = “person”):将配置文件中以person开头的属性值通过 setXX()方法注入到实体类对应属性中
  • @Component:将当前注入属性值的Person类对象作为Bean组件放到Spring容器中,只有这样才能被@ConfigurationProperties注解进行赋值
(2)编写需要对 Person类设置的配置属性
#自定义配置信息
person.id=1
person.name=王二麻子
person.hobby=read,write
person.family=father,mather
person.map.key1=value1
person.map.key2=value2
person.pet.type=dog
person.pet.name=哈士奇
(3)项目测试类
@RunWith(SpringRunner.class) // 测试启动器,并加载Spring Boot测试注解
@SpringBootTest // 标记为Spring Boot单元测试类,并加载项目的ApplicationContext上下文环境
class SpringbootDemoApplicationTests {
    // 配置测试
    @Autowired
    private Person person;
    
    @Test
    void configurationTest() {
        System.out.println(person);
    }
}

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

(4)中文乱码问题解决
  1. 调整文件编码格式:

在这里插入图片描述

  1. 设置Tomcat及Http编码
#解决中文乱码
server.tomcat.uri-encoding=UTF-8
spring.http.encoding.force=true
spring.http.encoding.charset=UTF-8
spring.http.encoding.enabled=true
1.6.2 application.yaml配置文件
  • YAML文件格式是Spring Boot支持的一种JSON文件格式

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

  • application.yml文件使用 “key:(冒号后面必须加空格)value”格式配置属性,使用缩进控制层级关系

  • 相较于传统的Properties配置文件,YAML文件以数据为核心,是一种更为直观且容易被电脑识别的数据序列化格式。

  • application.yaml配置文件的工作原理和application.properties是一样的,只不过yaml格式配置文件看起来更简洁一些

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

可以直接配置对应的属性值;同时对于字符串类型的属性值,不需要额外添加引号

server:
  port: 8080
  servlet:
    context-path: /hello
(2)value值为数组和单列集合

主要有两种书写方式:缩进式写法和行内式写法。

缩进式
  • 写法一(-(空格)属性值)
person:
  hobby:
    - play
    - read
    - sleep
  • 写法二(英文逗号分隔,最后一个属性值后不加)
person:
  hobby:
    play,
    read,
    sleep
行内式
person:
  hobby: [play,read,sleep]

行内式写法更加简明、方便。另外,包含属性值的中括 号“[]”还可以进一步省略,在进行属性赋值时,程序会自动匹配和校对

(3)value值为Map集合和对象

缩进式写法的形式按照YAML文件格式编写即可,而行内式写法的属性值要用大括号“{}”包含。

缩进式
person:
  map:
    k1: v1
    k2: v2
行内式
person:
  map: {k1: v1,k2: v2}

演示案例:

(1)编写为Person 类设置的配置属性

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

#对实体类对象Person进行属性配置
person:
  id: 1
  name: 王二麻子
  family:
    --hobby:
    - play
    - read
    - sleep
  map:
    k1: value1
    k2: value2
  pet:
    type:name: 哈士奇

1.7 配置文件属性值的注入

配置文件的优先级如下: 从低到高(按照下面的声明顺序)

<includes>
    <include>**/application*.yml</include>
    <include>**/application*.yaml</include>
    <include>**/application*.properties</include>
</includes>
1.7.1 使用@ConfigurationProperties注入属性

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

  • Spring Boot已有属性,如服务端口server.port,那么Spring Boot内部会自动扫描并读取这些配置文件中的属性值并覆盖默认属性。
  • 用户自定义属性,如自定义的Person实体类属性,还必须在程序中注入这些配置属性方可生效。
@Component
//将配置文件中所有以person开头的配置信息注入当前类中
//前提1:必须保证配置文件中person.xx与当前Person类的属性名一致
//前提2:必须保证当前Person中的属性都具有set方法
@ConfigurationProperties(prefix = "person")
public class Person {
    private int id; //id
    private String name; //名称
    private List hobby; //爱好
    private String[] family; //家庭成员
    private Map map;
    private Pet pet; //宠物
}
  • Spring Boot提供的@ConfigurationProperties注解用来快速、方便地将配置文件中的自定义属性值批量 注入到某个Bean对象的多个对应属性中
  • 使用@Component和@ConfigurationProperties(prefix = “person”)将配置文件中的每个属性 映射到person类组件中。
1.7.2 使用@Value注入属性
  • @Value注解是Spring框架提供的,用来读取配置文件中的属性值并逐个注入到Bean对象的对应属性中,Spring Boot框架从Spring框架中对@Value注解进行了默认继承,所以在Spring Boot框架中还可以使用该注解读取和注入配置文件属性值。
@Component
public class Person {
    @Value("${person.id}")
    private int id;
}
  • 使用@Component和@Value注入Person实体类的id属性。其中,@Value不仅可以 将配置文件的属性注入Person的id属性,还可以直接给id属性赋值,这点是@ConfigurationProperties 不支持的

  • 使用@Value注解方式需要对每一个属性注入设置,同时又免去了属性的 setXX()方法

  • @Value注 解对于包含Map集合、对象以及YAML文件格式的行内式写法的配置文件的属性注入都不支持,如果赋值会出现错误

1.8 自定义配置文件

Spring Boot会自动加载全局配置文件从而免除我们手动加载的烦恼。但是,如果我们自定义配置文件,Spring Boot是无 法识别这些配置文件的,此时就需要我们手动加载。

1.8.1 使用@PropertySource加载配置文件
  • @PropertySource注解用于指定自定义配置文件的具体位置 和名称

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

演示:

(1)resources目录下新建test.properties自定义配置文件

#对实体类对象MyProperties进行属性配置
test.id=110
test.name=test

(2)创建一个配置类com.lagou.pojo.MyProperties,提供test.properties自定义配置文件中对应的属性,使用@PropertySource注解进行相关配置

@Component // 自定义配置类
@PropertySource("classpath:test.properties") // 指定自定义配置文件位置和名称
@ConfigurationProperties(prefix = "test") // 指定配置文件注入属性前缀
public class MyProperties {
    private int id;
    private String name;
    // 省略属性getXX()和setXX()方法
    // 省略toString()方法
}
  • @PropertySource(“classpath:test.properties”)注解指定了自定义配置文件的位置和名称,此示例 表示自定义配置文件为classpath类路径下的test.properties文件;
  • @ConfigurationProperties(prefix = “test”)注解将上述自定义配置文件test.properties中以test开 头的属性值注入到该配置类属性中。

(3)进行测试

@Autowired
private MyProperties myProperties;
@Test
public void myPropertiesTest() {
    System.out.println(myProperties);
}
1.8.2 使用@Configuration编写自定义配置类

在Spring Boot框架中:

  • 推荐使用配置类的方式向容器中添加和配置组件
  • 通常使用@Configuration注解定义一个配置类,Spring Boot会自动扫描和识别配置类,从而替换传统Spring框架中的XML配置文件。

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

演示:

(1)新建com.lagou.config.MyService类,该类不需要编写任何代码

public class MyService {
}

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

(2) 新建com.lagou.config.MyConfig类,并使用@Configuration注解将该类声明 一个配置类

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

(3)测试类

@Autowired
private ApplicationContext applicationContext;
@Test
public void iocTest() {
    System.out.println(applicationContext.containsBean("myService"));
}

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

运行结果为true,表示Spirng的IOC容器中也已 经包含了id为myService的实例对象组件,说明使用自定义配置类的形式完成了向Spring容器进行组件的 添加和配置

2. SpringBoot原理深入及源码剖析

2.1 依赖管理

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

1.spring-boot-starter-parent依赖:版本统一管理

问题1:为什么导入dependency时不需要指定版本

<!-- Spring Boot父项目依赖管理 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</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.boot</groupId>
    <artifactId>spring-boot-dependencies</artifactId>
    <version>2.2.2.RELEASE</version>
    <relativePath>../../spring-boot-dependencies</relativePath>
</parent>

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

在这里插入图片描述

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

2. spring-boot-starter-web依赖:引入jar包

问题2: spring-boot-starter-parent父依赖启动器的主要作用是进行版本统一管理,那么项目运行依赖的JAR包是从何而来的

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

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
        <version>2.2.2.RELEASE</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-json</artifactId>
        <version>2.2.2.RELEASE</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-tomcat</artifactId>
        <version>2.2.2.RELEASE</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
        <version>2.2.2.RELEASE</version>
        <scope>compile</scope>
        <exclusions>
            <exclusion>
                <artifactId>tomcat-embed-el</artifactId>
                <groupId>org.apache.tomcat.embed</groupId>
            </exclusion>
        </exclusions>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>5.2.2.RELEASE</version>
        <scope>compile</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.2.RELEASE</version>
        <scope>compile</scope>
    </dependency>
</dependencies>

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

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

有哪些starter:
  • 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文件中引入这些第三方 的依赖启动器时,切记要配置对应的版本号

2.2 自动配置

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

问题:Spring Boot到底是如何进行自动配置的,都把哪些组件进行了自动配置?

  • Spring Boot应用的启动入口:@SpringBootApplication注解标注类中的main()方法,
  • @SpringBootApplication : SpringBoot 应用标注在某个类上说明这个类是 SpringBoot 的主配置类, SpringBoot 就应该运行这个类的 main() 方法启动 SpringBoot 应用。
@SpringBootApplication
public class SpringbootDemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootDemoApplication.class, args);
    }
}

进入到 @SpringBootApplication 内,观察其做了哪些工作:

//@Target、@Retention、@Documented、@Inherited 这四个注解描述的是元数据信息
@Target({ElementType.TYPE}) //注解的适用(作用)范围,Type表示注解可以描述在类、接口、注解或枚举中
@Retention(RetentionPolicy.RUNTIME) //表示注解的生命周期,Runtime运行时
@Documented //表示注解可以记录在javadoc中
@Inherited //表示可以被子类继承该注解
//核心注解
@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 个是注解的元数据信息, 我们主要看后面 三个核心注解:

  • @SpringBootConfiguration
  • @EnableAutoConfiguration
  • @ComponentScan
@SpringBootConfiguration

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

@SpringBootConfiguration注解核心代码:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration // 配置类的作用等同于配置文件,配置类也是容器中的一个对象
public @interface SpringBootConfiguration {
}
  • 核心注解@Configuration,由Spring框架提供,表示当前类为一个配置类(XML配置文件的注解表现形式),并可被组件扫描器扫描
  • @SpringBootConfiguration、@Configuration的作用相同:标识一个可以被组件扫描器扫描的配置类,只不过@SpringBootConfiguration是被Spring Boot进行了重新封装命名而已
@EnableAutoConfiguration

@EnableAutoConfiguration :开启自动配置功能,以前由我们需要配置的东西,现在由 SpringBoot 帮我们自动配置,是 Springboot 能实现自动配置的关键。

// 自动配置包
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited

// Spring的底层注解@Import,给容器中导入一个组件;
// 导入的组件是AutoConfigurationPackages.Registrar.class
@AutoConfigurationPackage
@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
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class}) // 导入Registrar中注册的组件
public @interface AutoConfigurationPackage {
}
  • @AutoConfigurationPackage的功能是由@Import实现的
  • @Import:spring框架的底层注解,给容器中导入某个组件类
  • @Import(AutoConfigurationPackages.Registrar.class),将Registrar这个组件类导入到容器中

Registrar.registerBeanDefinitions

在这里插入图片描述

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

因此 在定义项目包结构时,要求定义的包结构非常规范,才能够保证定义的类能够被组件扫描器扫描 :

  • 项目主程序启动类要定义在最外层的根目录位置
  • 在根目录位置内部建立子包和类进行业务开发
@Import({AutoConfigurationImportSelector.class})

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

1、AutoConfigurationImportSelector.selectImports这个方法告诉springboot都需要导入那些组件:

在这里插入图片描述

1-1、loadMetadata方法

在这里插入图片描述

1-2、AutoConfigurationImportSelector.getAutoConfigurationEntry

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

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

1-2-1-1、loadFactoryNames方法,读取一个 spring.factories 的文件,读取不到会表这个错误

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

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

spring.factories 的文件的路径在spring提供的一个工具类中是一个常量

public final class SpringFactoriesLoader {
    public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
}

它其实是去加载一个外部的文件:

在这里插入图片描述

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

3. SpringBoot数据访问

3.1 Spring Boot整合MyBatis

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

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

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

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

# 创建数据库
CREATE DATABASE springbootdata;
# 选择使用数据库
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)创建项目,引入相应的启动器

在这里插入图片描述

(3)编写实体类
public class Comment {
    private Integer id;
    private String content;
    private String author;
    private Integer aId;
}
public class Article {
    private Integer id;
    private String title;
    private String content;
}
(4)编写配置文件

在application.properties配置文件中进行数据库连接配置

# MySQL数据库连接配置
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC&characterEncoding=UTF-8
    username: root
    password: wu7787879
注解方式整合Mybatis

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

(1)CommentMapper
public interface CommentMapper {
    @Select("SELECT * FROM t_comment WHERE id =#{id}")
    public Comment findById(Integer id);
}
(2)向启动类添加@MapperScan
@SpringBootApplication
@MapperScan("com.lagou.mapper")
public class Springboot02MybatisApplication {
    public static void main(String[] args) {
        SpringApplication.run(Springboot02MybatisApplication.class, args);
    }
}
(3)测试
@RunWith(SpringRunner.class)
@SpringBootTest
class SpringbootPersistenceApplicationTests {
    
    @Autowired
    private CommentMapper commentMapper;
    
    @Test
    void contextLoads() {
        Comment comment = commentMapper.findById(1);
        System.out.println(comment);
    }
}

控制台中查询的Comment的aId属性值为null,没有映射成功。

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

开启驼峰命名匹配映射

在Spring Boot全局 配置文件application.properties中添加开启驼峰命名匹配映射配置

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

使用Free Mybatis plugin插件快速生成代码:

在这里插入图片描述

(1)ArticleMapper
@Mapper
public interface ArticleMapper {
    public Article selectArticle(Integer id);
}
(2)XML映射文件

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.lagou.mapper.ArticleMapper">
    <select id="selectArticle" resultType="Article">
        select * from Article
    </select>
</mapper>
(3)配置XML映射文件路径

在项目中编写的XML映射文件,Spring Boot无从知晓,所以无法扫描到该自定义编写的XML配置文件。

须在全局配置文件application.properties中添加MyBatis映射文件路径的配置,同时需要添加实体类别名映射路径

mybatis:
  configuration:
    map-underscore-to-camel-case: true #开启驼峰命名
  #就会将resources/mapper文件夹下的所有mapper文件加载
  mapper-locations: classpath:mapper/*.xml
  #配置xml映射文件中制定的实体类别名的扫描路径
  type-aliases-package: com.lagou.bootmybatis3.pojo
(4)测试
@Autowired
private ArticleMapper articleMapper;

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

3.2 Spring Boot整合Redis

(1)添加Redis依赖包
<!-- redis依赖包 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
(2)配置Redis数据库连接

在application.properties中配置redis数据库连接信息

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

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

package com.lagou.utils;

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

import java.util.concurrent.TimeUnit;

@Component
public class RedisUtils {
    
    @Autowired
    private RedisTemplate redisTemplate;
    
    /**
* 读取缓存
*
* @param key
* @return
*/
    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)测试
@RunWith(SpringRunner.class)
@SpringBootTest
class Springboot02MybatisApplicationTests {
    
    @Autowired
    private RedisUtils redisUtils;
    
    @Autowired
    private CommentMapper commentMapper;
    
    @Test
    public void setRedisData() {
        redisUtils.set("article_1",articleMapper.selectByPrimaryKey(1));
        System.out.println("success");
    }
    
    @Test
    public void getRedisData() {
        Article article = (Article) redisUtils.get("article_1");
        System.out.println(article);
    }
}

4. SpringBoot视图技术

4.1 支持的视图技术

前端模板引擎技术的出现,使前端开发人员无需关注后端业务的具体实现,只关注自己页面的呈现效果即可,并且解决了前端代码错综复杂的问题、实现了前后端分离开发。

  • 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要求在指定位置定制错误页面。

4.2 Thymeleaf

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

4.2.1 Thymeleaf语法
常用标签

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

<!DOCTYPE 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>Title</title>
    </head>
    <body>
        <p th:text="${hello}">欢迎进入Thymeleaf的学习</p>
    </body>
</html>
  • “xmlns:th=“http://www.thymeleaf.org”“ 用于引入Thymeleaf模板引擎标签
  • 使用关键字 “th”标注标签是Thymeleaf模板提供的标签
  • “th:href” :用于引入外联样式文件
  • “th:text” : 用于动态 显示标签文本内容

Thymeleaf的常用标签 :

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    用于指定标签显示的文本内容
标准表达式

Thymeleaf模板引擎提供了多种标准表达式语法,主要语法 及说明如下

变量表达式    ${...}
选择变量表达式 *{...}
消息表达式    #{...}
链接URL表达式 @{...}
片段表达式    ~{...}
1.变量表达式 ${…}

变量表达式${…}主要用于获取上下文中的变量值

<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}">US</span>.

示例使用th:text="${#locale.country}"动态获取当前用户所在国家信息:

  • 其中标签内默认 内容为US(美国)
  • 程序启动后通过浏览器查看当前页面时,Thymeleaf会通过浏览器语言设置来识别 当前用户所在国家信息,从而实现动态替换
2.选择变量表达式 *{…}

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

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

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

3.消息表达式 #{…}
  • 消息表达式#{…}主要用于Thymeleaf模板页面国际化内容的动态替换和展示,

  • 使用消息表达式#{…} 进行国际化设置时,还需要提供一些国际化配置文件。

4.链接表达式 @{…}

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

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

上述代码中,链接表达式@{…}分别编写了绝对链接地址和相对链接地址

  • 在有参表达式中,需要 按照@{路径(参数名称=参数值, 参数名称=参数值…)}的形式编写
  • 参数的值可以使用变量表达式 来传递动态参数值
5.片段表达式 ~{…}

片段表达式~{…}用来标记一个片段模板,并根据需要移动或传递给其他模板。

其中,最常见的用法是使用th:insert或th:replace属性插入片段

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

上述代码中,使用th:insert属性将title片段模板引用到该标签中:

  • thymeleafDemo为模板名称
  • title为片段名称
  • Thymeleaf会自动查找“/resources/templates/”目录下的 thymeleafDemo模板

4.2.2 基本使用

(1) Thymeleaf模板基本配置

引入Thymeleaf依赖

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

在全局配置文件中配置Thymeleaf模板的一些参数。一般Web项目都会使用下列配置

spring.thymeleaf.cache = true                   #启用模板缓存
spring.thymeleaf.encoding = UTF_8               #模板编码
spring.thymeleaf.mode = HTML5                   #应用于模板的模板模式
spring.thymeleaf.prefix = classpath:/templates/ #指定模板页面存放路径,默认值
spring.thymeleaf.suffix = .html                 #指定模板页面名称的后缀,默认值
  • spring.thymeleaf.cache表示是否开启Thymeleaf模板缓存,默认为true;

  • 在开发过程中通常会关闭缓存,保证项目调试过程中数据能够及时响应

(2)静态资源的访问

Spring boot默认设置了静态资源的访问路径:

  • 使用Spring Initializr方式创建的Spring Boot项目,默认生成了一个resources目录

  • 在resources目录中新建public、resources、static三个子目录下,

  • Spring boot默认会挨个从public、resources、static里面查找静态资源

4.2.3 完成数据的页面展示

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

在这里插入图片描述

2. 编写配置文件

打开application.properties全局配置文件,在该文件中对Thymeleaf模板页面的数据缓存进行设置

# thymeleaf页面缓存设置(默认为true),开发中方便调试应设置为false,上线稳定后应保持默认true
spring.thymeleaf.cache=false
3. 创建web控制类

com.lagou.controller.LoginController

@Controller
public class LoginController {
    /**
* 获取并封装当前年份跳转到登录页login.html
*/
    @RequestMapping("/toLoginPage")
    public String toLoginPage(Model model){
        model.addAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR));
        return "login";
    }
}

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

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

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

<!DOCTYPE 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="@{/login/css/bootstrap.min.css}" rel="stylesheet">
        <link th:href="@{/login/css/signin.css}" rel="stylesheet">
    </head>
    <body class="text-center">
        <!-- 用户登录form表单 -->
        <form class="form-signin">
            <img class="mb-4" th:src="@{/login/img/login.jpg}" 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}">2019</span>-
                <span th:text="${currentYear}+1">2020</span>
            </p>
        </form>
    </body>
</html>
  • 通过“xmlns:th=“http://www.thymeleaf.org””引入了Thymeleaf模板标签;
  • 使用“th:href”和“th:src”分别引入了两个外联的样式文件和一个图片;
  • 使用“th:text”引入了后台动态传递过来的当前年份currentYear

5. SpringBoot实战演练

lombok

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.12</version>
    <!--只在编译阶段生效-->
    <scope>provided</scope>
</dependency>

需求:实现用户的CRUD功能

(1)创建springboot工程

在这里插入图片描述

(2)导入pom.xml

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.3</version>
</dependency>

(3)User实体类编写

@Data
public class User implements Serializable {
    private Integer id;
    private String username;
    private String password;
    private String birthday;
    private static final long serialVersionUID = 1L;
}

(4)UserMapper编写及Mapper文件

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);
    List<User> queryAll();
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.base.mapper.UserMapper">
    <resultMap id="BaseResultMap" type="com.example.base.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>
    <select id="queryAll" resultType="com.example.base.pojo.User">
        select <include refid="Base_Column_List" />
        from user
    </select>
    .......
</mapper>

(5)UserService接口及实现类编写

package com.lagou.service.impl;
import com.lagou.mapper.UserMapper;
import com.lagou.pojo.User;
import com.lagou.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;
    @Override
    public List<User> queryAll() {
        return userMapper.queryAll();
    }
    @Override
    public User findById(Integer id) {
        return userMapper.selectByPrimaryKey(id);
    }
    @Override
    public void insert(User user) {
        //userMapper.insert(user); //将除id所有的列都拼SQL
        userMapper.insertSelective(user); //只是将不为空的列才拼SQL
    }
    @Override
    public void deleteById(Integer id) {
        userMapper.deleteByPrimaryKey(id);
    }
    @Override
    public void update(User user) {
        userMapper.updateByPrimaryKeySelective(user);
    }
}

(6)UserController编写

package com.lagou.controller;
import com.lagou.pojo.User;
import com.lagou.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.yaml.snakeyaml.events.Event;
import java.util.List;
@RestController
@RequestMapping("/user")
public class UserController {
    @Autowired
    private UserService userService;
    /**
* restful格式进行访问
* 查询:GET
* 新增: POST
* 更新:PUT
* 删除: DELETE
*/
    
    /**
* 查询所有
* @return
*/
    @GetMapping("/query")
    public List<User> queryAll(){
        return userService.queryAll();
    }
    
    /**
* 通过ID查询
*/
    @GetMapping("/query/{id}")
    public User queryById(@PathVariable Integer id){
        return userService.findById(id);
    }
    
    /**
* 删除
* @param id
* @return
*/
    @DeleteMapping("/delete/{id}")
    public String delete(@PathVariable Integer id){
        userService.deleteById(id);
        return "删除成功";
    }
    
    /**
* 新增
* @param user
* @return
*/
    @PostMapping("/insert")
    public String insert(User user){
        userService.insert(user);
        return "新增成功";
    }
    
    /**
* 修改
* @param user
* @return
*/
    @PutMapping("/update")
    public String update(User user){
        userService.update(user);
        return "修改成功";
    }
}

(8)application.yml

##服务器配置
server:
  port: 8090
  servlet:
    context-path: /
##数据源配置
spring:
  datasource:
    name: druid
    type: com.alibaba.druid.pool.DruidDataSource
    url: jdbc:mysql://localhost:3306/springbootdata?characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: wu7787879
#整合mybatis
mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml #声明Mybatis映射文件所在的位置

(9)启动类

@SpringBootApplication
//使用的Mybatis,扫描com.lagou.mapper
@MapperScan("com.lagou.mapper")
public class Springbootdemo5Application {
    public static void main(String[] args) {
        SpringApplication.run(Springbootdemo5Application.class, args);
    }
}

(10)使用Postman测试

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6. Spring Boot项目部署

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

  1. 添加打包组件
<build>
    <plugins>
        <!-- 打jar包时如果不配置该插件,打出来的jar包没有清单文件 -->
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>
  1. 需要添加打包组件将项目中的资源、配置、依赖包打到一个jar包中;可以使用maven的 package ;

  2. 部署运行

java -jar 包名
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值