Spring Boot 学习笔记

一、Spring Boot 入门

1. Spring Boot 简介

简化Spring应用开发的一个框架;

整个Spring技术栈的一个大整合;

J2EE开发的一站式解决方案;

2. 微服务

2014,martin fowler

微服务:架构风格(服务微化)

一个应用应该是一组小型服务;可以通过HTTP的方式进行互通;

单体应用:ALL IN ONE

微服务:每一个功能元素最终都是一个可独立替换和独立升级的软件单元;

详细参照微服务文档

3. 环境准备

http://www.gulixueyuan.com/ 谷粒学院

环境约束

  • jdk1.8:Spring Boot 推荐jdk1.7及以上

  • maven3.x:maven 3.3以上版本

  • IntelliJIDEA2018

  • SpringBoot 1.5.9.RELEASE

(1)MAVEN设置;

给maven 的settings.xml配置文件的profiles标签添加

1
2
3
4
5
6
7
8
9
10
11
12
<profile>
<id>jdk-1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>

(2)IDEA设置

IDEA整合maven

1554964739855

4. HelloWorld

一个功能:

浏览器发送hello请求,服务器接受请求并处理,响应Hello World字符串;

(1)创建一个maven工程

1554964942862

1554965030636

1554965043460

(2)导入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.qcmoke</groupId>
<artifactId>spring_01_helloworld</artifactId>
<version>1.0-SNAPSHOT</version>

<!-- Spring Boot的版本仲裁中心-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.9.RELEASE</version>
</parent>

<dependencies>
<!--选择场景启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>

<build>
<plugins>
<!-- 这个插件,可以将应用打包成一个可执行的jar包;-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

1、父项目spring-boot-starter-parent是场景启动器的版本仲裁中心,控制着SpringBoot的版本,跟换版本再次修改。

2、spring-boot-starter-web:spring-boot场景启动器;帮我们导入了web模块正常运行所依赖的组件。更多的场景启动器请查看官方文档

3、Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器

4、SpringBoot对J2EE的整体整合解决方案和自动配置都在spring-boot-autoconfigure-1.5.9.RELEASE.jar

(3)编写一个主程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.qcmoke;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
* 主程序类
*/
@SpringBootApplication
public class HelloWorldMainApplication {

public static void main(String[] args) {
// 启动Spring Boot应用
SpringApplication.run(HelloWorldMainApplication.class,args);
}
}
  • 1、@SpringBootApplication 来标注一个主程序类,说明这是一个Spring Boot应用

  • 2、Spring Boot会将主配置类(@SpringBootApplication标注的类)的所在包及该包下面所有子包里面的所有组件扫描到Spring容器中

    在本例中Spring Boot会到HelloWorldMainApplication所在的包即com.qcmoke包下和Controller包下扫描所有组件。由此就可以扫描到了com.qcmoke.Controller.HelloController组件

(4)编写相关的Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.qcmoke.Controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class HelloController {
@ResponseBody
@RequestMapping("/hello")
public String hello(){
return "Hello World!";
}
}

(5)运行主程序测试

1554965913914

(6)简化部署

1554966326970

将这个应用打成jar包,直接使用java -jar的命令可进行执行:

1
F:\myproject\springboot\spring_01_helloworld\target>java -jar spring_01_helloworld-1.0-SNAPSHOT.jar

1554966624221

部署完成

在此过程中不需要安装tomcat,因为springboot会将tomcat服务器直接嵌入到了生产的jar包中了。当运行这个jar包的时候,嵌入的tomcat也同样运行了起来。

1554966930716

浏览器输出http://localhost:8080/hello就可以访问部署的springboot应用了。

5. 使用Spring Initializer快速创建Spring Boot项目

(1)IDEA Spring Initializer

使用 Spring Initializer快速创建项目

IDE都支持使用Spring的项目创建向导快速创建一个Spring Boot项目。选择需要的模块,向导会联网创建Spring Boot项目。

1554971999513

1554973546305

选择场景启动器(依赖)

1554972282317

1554972317487

1554972346660

默认生成的Spring Boot项目的特点:

  • 主程序已经生成好了,我们只需要我们自己的逻辑
  • resources文件夹中目录结构
    • static:保存所有的静态资源; js css images;
    • templates:保存所有的模板页面;(Spring Boot默认jar包使用嵌入式的Tomcat,默认不支持JSP页面);可以使用模板引擎(freemarker、thymeleaf);
    • application.properties:Spring Boot应用的配置文件;可以修改一些默认设置;

此外还会附带一些版本控制和markdown等文件,由于当前项目用不到,故我们可以选择将这些文件删除掉。

1554972669969

编写相关的Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.qcmoke.controller;

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

//@Controller
@RestController //使用@RestController可以替换@Controller和@ResponseBody;直接返回数据给浏览器,如果返回的是对象,那么会转为json数据
public class HelloController {

//@ResponseBody
@RequestMapping("/hello")
public String hello(){
return "Hello World!";
}
}

运行测试

1554973841916

(2)STS

使用 Spring Starter Project快速创建项目

待完成…

二、springboot配置

1. 配置文件

SpringBoot默认使用的配置文件是resources目录下的application.properties和application.yml。要求文件名称是不能变的。而通过Spring Initializer创建的项目默认只生成一个名称为application.properties的配置文件。

(1)配置文件的作用

SpringBoot在底层都给我们自动配置好了默认值,可以通过修改配置文件从而修改SpringBoot自动配置的默认值。这些默认值包括端口等。

(2)配置文件语法介绍

由于properties文件较为普遍使用,这里对其语法就不进行过多讲解。

①YAML语法

YAML(YAML Ain’t Markup Language)

​ YAML A Markup Language:是一个标记语言

​ YAML isn’t Markup Language:不是一个标记语言;

标记语言:

​ 以前的配置文件;大多都使用的是 xxxx.xml文件;

​ YAML:以数据为中心,比json、xml等更适合做配置文件;

②基本语法使用

k:(空格)v:表示一对键值对(空格必须有);

空格的缩进来控制层级关系;只要是左对齐的一列数据,都是同一个层级的。

1
2
3
server:
port: 8081
path: /hello

注意:属性和值是大小写敏感的。

③值的写法

字面量:普通的值(数字,字符串,布尔)

k: v:字面直接来写;

​ 字符串默认不用加上单引号或者双引号;

"":双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思

name: "zhangsan \n lisi":输出;zhangsan 换行 lisi

'':单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据

name: ‘zhangsan \n lisi’:输出;zhangsan \n lisi

对象、Map(属性和值)(键值对),有两种方式

k: v:在下一行来写对象的属性和值的关系;注意缩进

​ 对象还是k: v的方式

1
2
3
friends:
lastName: zhangsan
age: 20

Ⅱ 行内写法:

1
friends: {lastName: zhangsan,age: 18}

数组(List、Set),有两种方式

Ⅰ 用-值表示数组中的一个元素

1
2
3
4
pets:
- cat
- dog
- pig

Ⅱ 行内写法

1
pets: [cat,dog,pig]

(3)测试yaml文件获取值注入到组件中

  • 组件:Person.java

  • 配置文件:application.yml

  • 方案:通过在组件中添加注解@ConfigurationProperties(prefix = “配置文件中的某个属性名称”)来让组件能够与配置文件中的某个属性建立关联

导入配置文件处理器依赖,让配置文件能够有提示

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

导入依赖完成需要重新运行springboot项目以生效。

要注入的组件类

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package com.qcmoke.bean;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;


/**
* 1、将配置文件中配置的每一个属性的值,映射到当前类组件中
* @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
* prefix = "person":配置文件中哪个下面的所有属性进行一一映射
*
* 2、只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能;
* @ConfigurationProperties(prefix = "person")默认从全局配置文件中获取值;
*
*/

@Component
@ConfigurationProperties(prefix = "person") //配置注入配置文件中person属性的值
public class Person {

/*生成get set方法;Alt + Insert 组合,如果不生效,先按NumLk键*/
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;

@Override
public String toString() {
return "Person{" +
"lastName='" + lastName + '\'' +
", age=" + age +
", boss=" + boss +
", birth=" + birth +
", maps=" + maps +
", lists=" + lists +
", dog=" + dog +
'}';
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public Boolean getBoss() {
return boss;
}

public void setBoss(Boolean boss) {
this.boss = boss;
}

public Date getBirth() {
return birth;
}

public void setBirth(Date birth) {
this.birth = birth;
}

public Map<String, Object> getMaps() {
return maps;
}

public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}

public List<Object> getLists() {
return lists;
}

public void setLists(List<Object> lists) {
this.lists = lists;
}

public Dog getDog() {
return dog;
}

public void setDog(Dog dog) {
this.dog = dog;
}
}

Dog.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.qcmoke.bean;

public class Dog {

private String name;
private Integer age;

@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}
}

编写配置文件,将配置文件中的属性值注入到Person对象里。

application.yml

1
2
3
4
5
6
7
8
9
10
11
12
13
person:
lastName: zhangsan #对于驼峰式的属性也可以改写成last-name: zhangsan
age: 22
boss: false
birth: 1996/08/27
maps: {k1: zhangsan,age: 22} #行内写法
lists: #数组写法
- 语文
- 数学
- 英语
dog: #对象换行写法
name: 旺财
age: 2

编写单元测试类

SpringBoot02ConfigApplicationTests.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.qcmoke;

import com.qcmoke.bean.Person;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/*
spring boot的单元测试
使用SpringRunner驱动器来执行测试,而不要junit
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBoot02ConfigApplicationTests {

@Autowired
Person person; //自动注入
@Test
public void contextLoads() {
System.out.println(person);
}

}

1554998203806

(4)测试properties配置文件获取值注入到组件中

  • 组件:Person.java

  • 配置文件:application.properties

  • 方案:通过在组件中添加注解@ConfigurationProperties(prefix = "配置文件中的某个属性名称")来让组件能够与配置文件中的某个属性建立关联

对于properties文件,需要解决idea中默认utf-8可能会输出乱码的问题。

➡️idea设置

1555033157548

导入配置文件处理器依赖,让配置文件能够有提示

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>

导入依赖完成需要重新运行springboot项目以生效。

要注入的组件类

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package com.qcmoke.bean;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;


/**
* 1、将配置文件中配置的每一个属性的值,映射到当前类组件中
* @ConfigurationProperties:告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定;
* prefix = "person":配置文件中哪个下面的所有属性进行一一映射
*
* 2、只有这个组件是容器中的组件,才能容器提供的@ConfigurationProperties功能;
* @ConfigurationProperties(prefix = "person")默认从全局配置文件中获取值;
*
*/

@Component
@ConfigurationProperties(prefix = "person") //配置注入配置文件中person属性的值
public class Person {

/*生成get set方法;Alt + Insert 组合,如果不生效,先按NumLk键*/
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;

@Override
public String toString() {
return "Person{" +
"lastName='" + lastName + '\'' +
", age=" + age +
", boss=" + boss +
", birth=" + birth +
", maps=" + maps +
", lists=" + lists +
", dog=" + dog +
'}';
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public Boolean getBoss() {
return boss;
}

public void setBoss(Boolean boss) {
this.boss = boss;
}

public Date getBirth() {
return birth;
}

public void setBirth(Date birth) {
this.birth = birth;
}

public Map<String, Object> getMaps() {
return maps;
}

public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}

public List<Object> getLists() {
return lists;
}

public void setLists(List<Object> lists) {
this.lists = lists;
}

public Dog getDog() {
return dog;
}

public void setDog(Dog dog) {
this.dog = dog;
}
}

Dog.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.qcmoke.bean;

public class Dog {

private String name;
private Integer age;

@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}
}

编写配置文件,将配置文件中的属性值注入到Person对象里。

application.properties

1
2
3
4
5
6
person.last-name=李四
person.age=21
person.birth=1996/08/27
person.boss=false
person.maps.k1=v1
person.maps.k2=v2

编写单元测试类

SpringBoot02ConfigApplicationTests.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.qcmoke;

import com.qcmoke.bean.Person;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/*
spring boot的单元测试
使用SpringRunner驱动器来执行测试,而不要junit
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBoot02ConfigApplicationTests {

@Autowired
Person person; //自动注入
@Test
public void contextLoads() {
System.out.println(person);
}

}

1555033539158

(5)@Value从配置文件获取值注入组件属性

  • 组件:Person.java
  • 配置文件:application.yml(或者application.properties)
  • 方案:通过在组件中添加注解@Value()来让组件能够与配置文件中的某个属性建立关联

以下没有修改的文件同上⬆️

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package com.qcmoke.bean;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Component
public class Person {
/**
* <bean class="Person">
* <property name="lastName" value="字面量/${key}从环境变量、配置文件中获取值/#{SpEL即SpringB表达式语言}"></property>
* <bean/>
*/
/*生成get set方法;Alt + Insert 组合,如果不生效,先按NumLk键*/
@Value("${person.last-name}")
private String lastName;
@Value("#{20+1}")
private Integer age;
private Boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;

@Override
public String toString() {
return "Person{" +
"lastName='" + lastName + '\'' +
", age=" + age +
", boss=" + boss +
", birth=" + birth +
", maps=" + maps +
", lists=" + lists +
", dog=" + dog +
'}';
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public Boolean getBoss() {
return boss;
}

public void setBoss(Boolean boss) {
this.boss = boss;
}

public Date getBirth() {
return birth;
}

public void setBirth(Date birth) {
this.birth = birth;
}

public Map<String, Object> getMaps() {
return maps;
}

public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}

public List<Object> getLists() {
return lists;
}

public void setLists(List<Object> lists) {
this.lists = lists;
}

public Dog getDog() {
return dog;
}

public void setDog(Dog dog) {
this.dog = dog;
}
}

(6)配置文件注入值数据校验

  • 组件:Person.java
  • 配置文件:application.properties(或者application.yml)
  • 方案:通过在组件中添加注解@ConfigurationProperties(prefix = “配置文件中的某个属性名称”)注入值来让组件能够与配置文件中的某个属性建立关联的同时使用@Validated和特定校验注解。

以下没有修改的文件同上⬆️

application.properties

1
2
3
4
5
6
person.last-name=lisi@qq.com
person.age=21
person.birth=1996/08/27
person.boss=false
person.maps.k1=v1
person.maps.k2=v2

Person.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
package com.qcmoke.bean;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;

import javax.validation.constraints.Email;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Component
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
@Email //注入的值必须是邮箱格式
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;

@Override
public String toString() {
return "Person{" +
"lastName='" + lastName + '\'' +
", age=" + age +
", boss=" + boss +
", birth=" + birth +
", maps=" + maps +
", lists=" + lists +
", dog=" + dog +
'}';
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public Boolean getBoss() {
return boss;
}

public void setBoss(Boolean boss) {
this.boss = boss;
}

public Date getBirth() {
return birth;
}

public void setBirth(Date birth) {
this.birth = birth;
}

public Map<String, Object> getMaps() {
return maps;
}

public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}

public List<Object> getLists() {
return lists;
}

public void setLists(List<Object> lists) {
this.lists = lists;
}

public Dog getDog() {
return dog;
}

public void setDog(Dog dog) {
this.dog = dog;
}
}

(7)@Value获取值和@ConfigurationProperties获取值比较

@ConfigurationProperties @Value
功能 批量注入配置文件中的属性 一个个指定
松散绑定(松散语法),例如属性名称为驼峰或者横线不敏感 支持 不支持
SpEL 不支持 支持
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持

配置文件yml还是properties都能获取到值。

@Value和@ConfigurationProperties的使用场景:

如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,可选择使用@Value。

如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接选择使用@ConfigurationProperties。

(8)@PropertySource

  • 作用 : 加载指定的自定义配置文件。

  • 使用背景:@ConfigurationProperties默认加载的是全局配置文件,如果全部的配置都写到主配置文件里,那么主配置文件将会越来越难维护。我们可以将与框架无关的配置写在非主配置文件中,尽量解耦。

  • 使用方法:在组件中添加注解@PropertySource(value = {"自定义配置文件路径"})来指定自定义配置文件并且配合@ConfigurationProperties(prefix = "属性名")指定具体属性。

  • 案例:

以下所有未说明的文件同上次案例。

①注释掉所有主配置文件的person相关配置。

person.properties(自定义配置文件)

1
2
3
4
5
6
person.last-name=wangwu@qq.com
person.age=21
person.birth=1996/08/27
person.boss=false
person.maps.k1=v1
person.maps.k2=v2

Person.java(组件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
package com.qcmoke.bean;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import java.util.Date;
import java.util.List;
import java.util.Map;

@PropertySource(value = {"classpath:person.properties"})//加载指定的自定义配置文件
@ConfigurationProperties(prefix = "person")
@Component
public class Person {
private String lastName;
private Integer age;
private Boolean boss;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;

@Override
public String toString() {
return "Person{" +
"lastName='" + lastName + '\'' +
", age=" + age +
", boss=" + boss +
", birth=" + birth +
", maps=" + maps +
", lists=" + lists +
", dog=" + dog +
'}';
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public Boolean getBoss() {
return boss;
}

public void setBoss(Boolean boss) {
this.boss = boss;
}

public Date getBirth() {
return birth;
}

public void setBirth(Date birth) {
this.birth = birth;
}

public Map<String, Object> getMaps() {
return maps;
}

public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}

public List<Object> getLists() {
return lists;
}

public void setLists(List<Object> lists) {
this.lists = lists;
}

public Dog getDog() {
return dog;
}

public void setDog(Dog dog) {
this.dog = dog;
}
}

(9)@ImportResource

  • 作用:导入spring配置文件(.xml),让配置文件里面的内容生效;

  • 使用方法:自己编写的spring配置文件.xml配置文件,不能自动识别;想让xml配置文件生效,可使用@ImportResource(locations = {"spring配置文件xml的路径"})标注在一个配置类上加载进来。

案例:

以下没有修改的文件同上⬆️

beans.xml

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="personService" class="com.qcmoke.service.PersonService"></bean>
</beans>

PersonService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.qcmoke.service;

import com.qcmoke.bean.Person;
import org.springframework.beans.factory.annotation.Autowired;

public class PersonService {
@Autowired
Person person;

public Person getPerson(){
return person;
}
}

SpringBoot02Config5Application.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.qcmoke;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

@ImportResource(locations = {"classpath:beans.xml"})//在主配置类上加载beans.xml
@SpringBootApplication
public class SpringBoot02Config5Application {

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

}

SpringBoot02Config5ApplicationTests.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package com.qcmoke;

import com.qcmoke.bean.Person;
import com.qcmoke.service.PersonService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/*
spring boot的单元测试
使用SpringRunner驱动器来执行测试,而不要junit
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBoot02Config5ApplicationTests{

@Autowired
PersonService service;

@Test
public void contextLoads() {
System.out.println(service.getPerson());
}
}

(10)配置文件占位符

propertiesyaml配置文件中是可以使用占位符的

①随机数
  • ${random.value} - 类似uuid的随机数,没有"-"连接
  • ${random.int} - 随机取整型范围内的一个值
  • ${random.long} - 随机取长整型范围内的一个值
  • ${random.long(100,200)} - 随机生成长整型100-200范围内的一个值
  • ${random.uuid} - 生成一个uuid,有短杠连接
  • ${random.int(10)} - 随机生成一个10以内的数
  • ${random.int(100,200)} - 随机生成一个100-200 范围以内的数
②占位符
  • ${key:defaultValue} - 若key 不存在,则将defaultValue的值赋值给取值的对象

案例:

除以下特别说明的文件需要修改,其他文件同上⬆️

person.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
person.last-name=${random.uuid}@qq.com

#随机生成从0到120的整型值
person.age=${random.int(0,120)}

person.birth=1996/${random.int(12)}/27

person.boss=false

#随机生成10以内得整型值
person.maps.k1=${random.int(10)}

#类似uuid的随机数,没有"-"连接
person.maps.k2=${random.value}

#获取配置属性的值进行占位
person.dog.name=${person.last-name}_dog

#先取age的值给person.dog.age,如果上面没有age的属性和值,那么就取默认值2
person.dog.age=${age:2}

2. 多环境配置

springboot默认激活的的是主配置文件application.properties/yml。但很多场景的配置,比如数据库配置、Redis 配置、注册中心和日志配置等,在不同的环境,我们需要不同的包去运行项目,那么就需要有多种配置方案,而且希望不同配置文件管理不同的环境配置,这种情况,文件名只要是application-{profile}.properties/yml,然后在主配置文件application.properties/yml通过以下配置来激活即可。

1
2
3
spring:
profiles:
active: {profile}

案例:

多环境配置文件有:

  • application-dev.yml (开发环境)
1
2
server:
port: 8081
  • application-prod.yml (生产环境)
1
2
server:
port: 80

激活不同环境下的配置文件(三种方式)

(1)主配置文件指定方式

application.yml

1
2
3
spring:
profiles:
active: dev

(2)命令行:

1
java -jar spring-boot-02-config-0.0.1-SNAPSHOT.jar --spring.profiles.active=dev;

也可以直接在测试的时候,ide中配置传入命令行参数

(3)虚拟机参数;

1
-Dspring.profiles.active=dev

3. 多文档块配置

要求配置文件是yml格式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server:
port: 8081
spring:
profiles:
active: prod

---
server:
port: 8083
spring:
profiles: dev


---

server:
port: 8084
spring:
profiles: prod #指定属于哪个环境

4. 配置文件加载位置

4.1 加载优先级

springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件,优先级由高到底,高优先级的配置会覆盖低优先级的配置;

–file:./config/
–file:./
–classpath:/config/
–classpath:/

如果以上4个位置都有主配置文件,那么SpringBoot会从这四个位置全部加载主配置文件,达到互补配置的效果;

4.2 自定义加载位置

我们还可以通过spring.config.location来改变默认的配置文件位置

项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;

1
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --spring.config.location=G:/application.properties

5. 外部配置加载顺序

SpringBoot也可以从以下位置加载配置; 优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置。

1.命令行参数

所有的配置都可以在命令行上进行指定

1
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087  --server.context-path=/abc

多个配置用空格分开; --配置项=值

2.来自java:comp/env的JNDI属性

3.Java系统属性(System.getProperties())

4.操作系统环境变量

*5.RandomValuePropertySource配置的random.属性值

6.jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件

7.jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件

8.jar包外部的application.properties或application.yml(不带spring.profile)配置文件

9.jar包内部的application.properties或application.yml(不带spring.profile)配置文件

10.@Configuration注解类上的@PropertySource

11.通过SpringApplication.setDefaultProperties指定的默认属性

6. 配置类

在spring中,给spring容器添加组件有两种方式:

  1. 通过配置文件添加组件 (前面已经介绍)
  2. 通过配置类的方式添加组件

而SpringBoot推荐给容器中添加组件的方式是使用全注解的方式,也就是配置类的方式。

(1)配置类添加组件

会用到两个重要的注解

  • @Configuration 指明当前类是一个配置类,相当于spring.xml
  • @Bean 将方法的返回值添加到容器中。默认的bean id就是方法名

(2)案例

以下没有修改的文件同上⬆️

(1)创建配置类

PersonConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.qcmoke.config;

import com.qcmoke.service.PersonService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* @Configuration 指明当前类是一个配置类,相当于spring.xml
* @Bean 将方法的返回值添加到容器中。默认的bean id就是方法名
*/
@Configuration
public class PersonConfig {
@Bean
public PersonService personService(){
return new PersonService();
}
}

(2)取消主配置文件的@ImportResource注解

SpringBoot02Config5Application.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package com.qcmoke;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ImportResource;

//@ImportResource(locations = {"classpath:beans.xml"})//在主配置类上加载beans.xml
@SpringBootApplication
public class SpringBoot02Config5Application {

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

}

三、日志

1. 市面上的日志框架

JUL、JCL、Jboss-logging、logback、log4j、log4j2、slf4j…

日志门面 (日志的抽象层) 日志实现
JCL(Jakarta Commons Logging) SLF4j(Simple Logging Facade for Java) jboss-logging Log4j JUL(java.util.logging) Log4j2 Logback

左边选一个门面(抽象层)、右边来选一个实现;

日志门面: SLF4J;

日志实现:Logback;

SpringBoot:底层是Spring框架,Spring框架默认是用JCL而SpringBoot选用 SLF4j和logback。

每一个日志的实现框架都有自己的配置文件。使用slf4j以后,配置文件还是做成日志实现框架自己本身的配置文件;

2. 遗留问题

Spring(commons-logging)、Hibernate(jboss-logging)、MyBatis、xxxx

统一日志记录,即使是别的框架和我一起统一使用slf4j进行

legacy

3. 统一日志记录

如何让系统中所有的日志都统一到slf4j;

1、将系统中其他日志框架先排除出去

2、用中间包来替换原有的日志框架

3、我们导入slf4j其他的实现

4. 使用日志

SpringBoot底层也是使用slf4j+logback的方式进行日志记录。并且也把其他的日志都替换成了slf4j,并且都已经配有日志配置默认值。开发的时候直接使用即可。

测试:

SpringBoot03LogApplicationTests.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package com.qcmoke;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringBoot03LogApplicationTests {
//记录器
Logger logger = LoggerFactory.getLogger(getClass());
@Test
public void contextLoads() {

//日志的级别;
//由低到高 trace<debug<info<warn<error
//可以调整输出的日志级别;日志就只会在这个级别以以后的高级别生效
logger.trace("这是trace日志...");
logger.debug("这是debug日志...");
//SpringBoot默认给我们使用的是info级别的,没有指定级别的就用SpringBoot默认规定的级别;root级别
logger.info("这是info日志...");
logger.warn("这是warn日志...");
logger.error("这是error日志...");
}
}

5. SpringBoot修改日志的默认配置

application.properties

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
logging.level.com.atguigu=trace


#logging.path=
# 不指定路径在当前项目下生成springboot.log日志
# 可以指定完整的路径;
#logging.file=G:/springboot.log

# 在当前磁盘的根路径下创建spring文件夹和里面的log文件夹;使用 spring.log 作为默认文件
logging.path=/spring/log

# 在控制台输出的日志的格式
logging.pattern.console=%d{yyyy-MM-dd} [%thread] %-5level %logger{50} - %msg%n
# 指定文件中日志输出的格式
logging.pattern.file=%d{yyyy-MM-dd} === [%thread] === %-5level === %logger{50} ==== %msg%n

logging.file和logging.path

logging.file logging.path Example Description
(none) (none) 只在控制台输出
指定文件名 (none) my.log 输出日志到my.log文件
(none) 指定目录 /var/log 输出到指定目录的 spring.log 文件中

查看springboot默认日志配置的目录

spring-boot-2.1.4.RELEASE.jar!/org/springframework/boot/logging/logback/

6. 自定义日志配置

给类路径下放上日志框架自己的配置文件即可;SpringBoot就不使用他默认配置的了。以下表格是各类日志框架的配置文件名规范。

Logging System Customization
Logback logback-spring.xml, logback-spring.groovy, logback.xml or logback.groovy
Log4j2 log4j2-spring.xml or log4j2.xml
JDK (Java Util Logging) logging.properties

对于Logback日志框架的另外说明,两种命名规则的差别。

  • logback.xml:直接就被日志框架识别了。

  • logback-spring.xml(建议使用):日志框架就不直接加载日志的配置项,由SpringBoot解析日志配置,可以使用SpringBoot的高级Profile功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!--
日志输出格式:
%d表示日期时间,
%thread表示线程名,
%-5level:级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,
%n是换行符
-->
<layout class="ch.qos.logback.classic.PatternLayout">
<springProfile name="dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ----> [%thread] ---> %-5level %logger{50} - %msg%n</pattern>
</springProfile>
<springProfile name="!dev">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} ==== [%thread] ==== %-5level %logger{50} - %msg%n</pattern>
</springProfile>
</layout>
</appender>

如果使用logback.xml作为日志配置文件,还要使用profile功能,可能会报错

logback-spring.xml配置详情解释

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration scan="false" scanPeriod="60 seconds" debug="false">
<!-- 定义日志的根目录 -->
<property name="LOG_HOME" value="/app/log" />
<!-- 定义日志文件名称 -->
<property name="appName" value="atguigu-springboot"></property>
<!-- ch.qos.logback.core.ConsoleAppender 表示控制台输出 -->
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">
<!--
日志输出格式:
%d表示日期时间,
%thread表示线程名,
%-5level:级别从左显示5个字符宽度
%logger{50} 表示logger名字最长50个字符,否则按照句点分割。
%msg:日志消息,
%n是换行符
-->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} -----》 [%thread] 《----- %-5level %logger{50} - %msg%n</pattern>
</layout>
</appender>

<!-- 滚动记录文件,先将日志记录到指定文件,当符合某个条件时,将日志记录到其他文件 -->
<appender name="appLogAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 指定日志文件的名称 -->
<file>${LOG_HOME}/${appName}.log</file>
<!--
当发生滚动时,决定 RollingFileAppender 的行为,涉及文件移动和重命名
TimeBasedRollingPolicy: 最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动。
-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!--
滚动时产生的文件的存放位置及文件名称 %d{yyyy-MM-dd}:按天进行日志滚动
%i:当文件大小超过maxFileSize时,按照i进行文件滚动
-->
<fileNamePattern>${LOG_HOME}/${appName}-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
<!--
可选节点,控制保留的归档文件的最大数量,超出数量就删除旧文件。假设设置每天滚动,
且maxHistory是365,则只保存最近365天的文件,删除之前的旧文件。注意,删除旧文件是,
那些为了归档而创建的目录也会被删除。
-->
<MaxHistory>365</MaxHistory>
<!--
当日志文件超过maxFileSize指定的大小是,根据上面提到的%i进行日志文件滚动 注意此处配置SizeBasedTriggeringPolicy是无法实现按文件大小进行滚动的,必须配置timeBasedFileNamingAndTriggeringPolicy
-->
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
<!-- 日志输出格式: -->
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [ %thread ] - [ %-5level ] [ %logger{50} : %line ] - %msg%n</pattern>
</layout>
</appender>

<!--
logger主要用于存放日志对象,也可以定义日志类型、级别
name:表示匹配的logger类型前缀,也就是包的前半部分
level:要记录的日志级别,包括 TRACE < DEBUG < INFO < WARN < ERROR
additivity:作用在于children-logger是否使用 rootLogger配置的appender进行输出,
false:表示只用当前logger的appender-ref,true:
表示当前logger的appender-ref和rootLogger的appender-ref都有效
-->
<!-- hibernate logger -->
<logger name="com.atguigu" level="debug" />
<!-- Spring framework logger -->
<logger name="org.springframework" level="debug" additivity="false"></logger>



<!--
root与logger是父子关系,没有特别定义则默认为root,任何一个类只会和一个logger对应,
要么是定义的logger,要么是root,判断的关键在于找到这个logger,然后判断这个logger的appender和level。
-->
<root level="info">
<appender-ref ref="stdout" />
<appender-ref ref="appLogAppender" />
</root>
</configuration>

7. 切换日志框架

四、web开发

1. 静态资源的映射规则

定义规则的类为:org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfigurationorg.springframework.boot.autoconfigure.web.ResourceProperties

  1. 所有/webjars/** ,都去classpath:/META-INF/resources/webjars/找资源

webjars:以jar包的方式引入静态资源。http://www.webjars.org/

例如引入jquery

1
2
3
4
5
6
<!--jquery-webjar-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>

那么访问jquery的地址是http://localhost:8080/webjars/jquery/3.3.1/jquery.js

  1. “/**” 访问当前项目的任何资源,都去(静态资源的文件夹)找映射,以下就是springboot默认的静态资源目录。
1
2
3
4
5
"classpath:/META-INF/resources/", 
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
"/":当前项目的根路径
  1. index.html页面会被"/**“映射,默认匹配”/"。

  2. 所有的 **/favicon.ico(页面图标)都是在静态资源文件下找

2. 自定义静态资源目录

当启用自定义的静态资源目录后,那么默认的静态资源目录就会失效。

如以下将classpath:/mystatic/classpath:/mypublic/设置为静态资源目录,那么classpath:/META-INF/resources/, classpath:/resources/,classpath:/static/, classpath:/public/,/等静态资源目录就会失效。

application.properties

1
spring.resources.static-locations=classpath:/mystatic/,classpath:/mypublic/

3. 模板引擎

JSP(由于springboot默认是以jar包的方式打包,所有默认不支持jsp)、Velocity、Freemarker、Thymeleaf

template-engine

SpringBoot推荐的Thymeleaf;

语法更简单,功能更强大;

3.1 Thymeleaf

(1)引入thymeleaf
1
2
3
4
5
6
7
8
9
10
11
12
13
<properties>
<!--配置thymeleaf版本-->
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version> <!-- 布局功能的支持程序 thymeleaf3主程序要layout2以上版本 -->
</properties>

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

完整pom.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qcmoke</groupId>
<artifactId>spring_boot_03_web</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_boot_03_web</name>
<description>Demo project for Spring Boot</description>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>

<!--配置thymeleaf版本-->
<thymeleaf.version>3.0.9.RELEASE</thymeleaf.version>
<thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version> <!-- 布局功能的支持程序 thymeleaf3主程序要layout2以上版本 -->
</properties>

<dependencies>
<!--springboot web模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!--springboot测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!--jquery-webjar-->
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.3.1</version>
</dependency>

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

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

</project>

注:以上thymeleaf需要springboot1.5.10.RELEASE版本,高版本可能会出错

(2)表达式
1
2
3
4
5
${...} : 变量表达式。OGNL表达式
*{...} : 选择表达式。
#{...} : 消息 (i18n) 表达式。
@{...} : 链接 (URL) 表达式。
~{...} : 片段表达式。

表达式都作为标签的属性值。更多表达式介绍可参阅官方文档或者https://www.e-learn.cn/thymeleaf/standard-dialects

(3) 语法

2018-02-04_123955

更多语法参阅官方文档或者https://www.kancloud.cn/cxr17618/springboot/428898

(4)测试thymeleaf

只要我们把编写有thymeleaf语法的HTML页面放在classpath:/templates/thymeleaf就能自动渲染。

SuccessController.java(目的是给thymeleaf页面提供动态数据)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.qcmoke.controller;

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

import java.util.Arrays;
import java.util.Map;

@Controller
public class SuccessController {

//查出用户数据,在页面展示
@RequestMapping("/success")
public String success(Map<String,Object> map){
map.put("hello","<h1>你好</h1>");//动态数据
map.put("users",Arrays.asList("qcmoke","zhangsan","lisi"));
return "success";
}
}

success.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org"> <!--导入thymeleaf的名称空间,有更好的thymeleaf语法提示效果-->
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--th:text不转译特殊字符-->
<div th:utext="${hello}">这是显示欢迎信息</div><!--将div里面的文本内容设置为hello动态变量的值-->


<!--th:text转译特殊字符-->
<div th:text="${hello}"></div>


<!--th:each-->
<h4 th:text="${user}" th:each="user:${users}"></h4><!--每遍历一次就会生成一个h4标签-->
<h4>
<span th:text="${user}" th:each="user:${users}"></span><!--每遍历一次就会生成一个span标签-->
</h4>

<!--行内表达式,取值并显示-->
<h4>
<span th:each="user:${users}"> [[${user}]] </span>
</h4>

</body>
</html>

3.2 Jsp

springboot内置servlet服务器默认不支持以启动springboot应用即main方法启动的方式解析器jsp。为了解决这种问题。有两种决绝方案。

3.2.1 外置servlet服务器方案

(1)创建项目的打包方式位war

(2)添加依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>

完整pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qcmoke</groupId>
<artifactId>spring_boot_03_web_support_jsp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>spring_boot_03_web_support_jsp</name>
<description>Demo project for Spring Boot</description>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

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

<!--外置tomcat支持jsp方案-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>


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

</project>

(3)完善项目结构和配置tomcat

生成webapp

1555643363632

生成web.xml

修改web.xml路径为项目地址\src\main\webapp\WEB-INF\web.xml

1555644178275

配置tomcat

1555642051734

1555642088639

1555642178396

部署web项目

1555642326550

1555642352102

1555642365580

(4)编写一个SpringBootServletInitializer的子类,并调用configure方法(必须),作用是在tomcat启动同时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.qcmoke;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.support.SpringBootServletInitializer;
/**
* Web程序启动类
*/
public class ServletInitializer extends SpringBootServletInitializer {

@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringBoot03WebSupportJspApplication.class);
}
}

(3)配置application.properties

1
2
3
# 配置视图解析器,配置jsp的路径和后缀
spring.mvc.view.prefix=/WEB-INF/
spring.mvc.view.suffix=.jsp

(4)编写测试代码

UserController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.qcmoke.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

@Controller
public class UserController {
@RequestMapping("/tomain")
public String tomain(Model model, Map<String,Object> map){
model.addAttribute("name","qcmoke");
map.put("age",22);
return "success";
}
}

WEB-INF/success.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<%--
Created by IntelliJ IDEA.
User: qcmoke
Date: 2019/4/19
Time: 1:20
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
hello ${name},your age is ${age} !
</body>
</html>

(5)不以main方法启动,而是点击run按钮运行部署web项目

1555642475343

3.2.2 内置servlet服务器加插件方案

(1)打包方式为jar或者war(建议为jar)

(2)添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
<!--内嵌tomcat支持jsp方案1(推荐)-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>

或者

1
2
3
4
5
6
7
8
9
10
11
<!--内嵌tomcat支持jsp方案2-->
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.16</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>8.5.16</version>
</dependency>

完整pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qcmoke</groupId>
<artifactId>spring_boot_web_jsp2</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_boot_web_jsp2</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
</properties>

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

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

<!--内嵌tomcat支持jsp方案1-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
<scope>provided</scope>
</dependency>

<!--内嵌tomcat支持jsp方案2-->
<!--<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-core</artifactId>
<version>8.5.16</version>
</dependency>
<dependency>
<groupId>org.apache.tomcat</groupId>
<artifactId>tomcat-jasper</artifactId>
<version>8.5.16</version>
</dependency>-->
</dependencies>

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

</project>

(3)完善项目结构和配置tomcat(同上

(4)配置application.properties

1
2
3
# 配置视图解析器,配置jsp的路径和后缀
spring.mvc.view.prefix=/WEB-INF/
spring.mvc.view.suffix=.jsp

(5)编写测试代码

UserController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.qcmoke.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import java.util.Map;

@Controller
public class UserController {
@RequestMapping("/tomain")
public String tomain(Model model, Map<String,Object> map){
model.addAttribute("name","qcmoke");
map.put("age",22);
return "success";
}
}

WEB-INF/success.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
hello ${name},your age is ${age} !
</body>
</html>

(6)运行main方法启动springboot应用

3.3 jar和war打包方式启动区别

  • jar包:执行SpringBootApplication的run方法,启动IOC容器,然后创建嵌入式Servlet容器。

  • war包: 先是启动外置Servlet服务器,服务器就会启动Springboot应用(springBootServletInitizer),然后Springboot应用再启动IOC容器。

4. SpringMVC自动配置

官方文档

1. Spring MVC auto-configuration

Spring Boot 默认已经自动配置好了SpringMVC,直接使用即可。

  • 自动配置了视图解析器

    • 自定义视图解析器(如需要)

    例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.qcmoke.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.ViewResolver;

import java.util.Locale;

@SpringBootApplication
public class DemoApplication {

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


/**
* 自定义视图解析器
*/
public static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String viewName, Locale locale) throws Exception {
return null;
}
}

/**
* 将自定义的视图解析器放到spring容器中
*/
@Bean
public ViewResolver myViewReolver(){
return new MyViewResolver();
}

}
  • 自动注册了 Converter, GenericConverter, Formatter格式化器 等转换器.

2. 如何修改SpringBoot的默认web配置

2.1 模式

​ 1)、SpringBoot在自动配置很多组件的时候,先看容器中有没有用户自己配置的(@Bean、@Component)如果有就用用户配置的,如果没有,才自动配置;如果有些组件可以有多个(如ViewResolver),那么SpringBoot会将用户配置的和自己默认的配置的组合起来。

​ 2)、在SpringBoot中会有非常多的xxxConfigurer帮助我们进行扩展配置

​ 3)、在SpringBoot中会有很多的xxxCustomizer帮助我们进行定制配置

2.2 默认访问首页
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//继承WebMvcConfigurerAdapter类可以来扩展SpringMVC的功能
//@EnableWebMvc 配置不要完全接管SpringMVC
@Configuration
public class MyMvcConfig extends WebMvcConfigurerAdapter {

@Override
public void addViewControllers(ViewControllerRegistry registry) {
// super.addViewControllers(registry);
//浏览器发送 /atguigu 请求来到 success
registry.addViewController("/atguigu").setViewName("success");
}

//所有的WebMvcConfigurerAdapter组件都会一起起作用
@Bean //将组件注册在容器
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("login");
registry.addViewController("/index.html").setViewName("login");
}
};
return adapter;
}
}

五、持久层开发

1. 常用数据源配置

1.1 默认数据源springboot jdbc

  • springboot默认是使用org.apache.tomcat.jdbc.pool.DataSource作为连接池(数据源)。

  • SpringBoot默认可以支持的连接池包括org.apache.tomcat.jdbc.pool.DataSource、HikariDataSource、BasicDataSource。

下面将介绍使用默认提供的org.apache.tomcat.jdbc.pool.DataSource作为连接池(数据源)测试数据库访问。

(1)数据表

resources/department.sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for department
-- ----------------------------
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`departmentName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of department
-- ----------------------------
INSERT INTO `department` VALUES (1, '研发部');

SET FOREIGN_KEY_CHECKS = 1;
(2)引入依赖

需要引入springboot jdbc依赖和数据库驱动包

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

完整依赖:

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.qcmoke</groupId>
<artifactId>spring_boot_04_jdbc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_boot_04_jdbc</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>1.8</java.version>
</properties>

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

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

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>

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

</project>
(3)配置数据库连接信息

application.yml

1
2
3
4
5
6
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
(4)测试数据库链接

ApplicationTests.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.qcmoke;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {

/**
* 默认是用org.apache.tomcat.jdbc.pool.DataSource作为数据源;
*/

@Autowired
DataSource dataSource;
@Test
public void contextLoads() throws SQLException {

Connection connection = dataSource.getConnection();
System.out.println(connection!=null?"conn success":"conn fail");
connection.close();

}

}

1.2 Druid数据源

(1)数据表

resources/department.sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for department
-- ----------------------------
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`departmentName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of department
-- ----------------------------
INSERT INTO `department` VALUES (1, '研发部');

SET FOREIGN_KEY_CHECKS = 1;

如果不配置,那么默认连接池为tomcat.jdbc.pool.DataSource。

(2)引入依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!--springboot jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--引入druid数据源-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
<!--mysql 驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- <version>8.0.11</version>--><!--mysql8.0需要指定高版本的驱动包-->
<scope>runtime</scope>
</dependency>

完整依赖:

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.qcmoke</groupId>
<artifactId>spring_boot_04_druid</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_boot_04_jdbc</name>
<description>Demo project for Spring Boot</description>

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

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>

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



<!--springboot jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--引入druid数据源-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
<!--mysql 驱动包-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<!-- <version>8.0.11</version>--><!--mysql8.0需要指定高版本的驱动包-->
<scope>runtime</scope>
</dependency>

</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork>
</configuration>
</plugin>
</plugins>
</build>

</project>
(3)配置数据库连接信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://47.101.174.33:3306/jdbc?useUnicode=true&characterEncoding=utf8&useSSL=false
driver-class-name: com.mysql.jdbc.Driver
schema: #运行sql脚本文件,如果不存在则创建,如果存在不进行操作(可选)
- classpath:department.sql
type: com.alibaba.druid.pool.DruidDataSource #指定数据源类型,默认是使用org.apache.tomcat.jdbc.pool.DataSource作为连接池(数据源)
#druid可选的属性配置,默认比不起作用,如果需要这些可选配置需要new DruidDataSource()到spring容器中覆盖掉原来默认的数据源。并通过@ConfigurationProperties(prefix = "spring.datasource")的方式将datasource的属性包括可选注入到这个新建的实例对象中
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
filters: stat,wall,log4j # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

druid可选的属性配置(如initialSize、minIdle等),默认比不起作用(但不会报错),如果需要这些可选配置需要new DruidDataSource()到spring容器中覆盖掉原来默认的数据源。并通过@ConfigurationProperties(prefix = "spring.datasource")的方式将datasource的属性包括可选注入到这个新建的实例对象中。

src/main/java/com/qcmoke/config/DruidConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.qcmoke.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DruidConfig {

/**
* 创建数据源
*/
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid(){
return new DruidDataSource();
}

/**
* 配置Druid的监控web页面
* druid监控地址http://localhost:8080/druid/login.html
* @return
*/

//1、配置一个管理后台的Servlet
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
Map<String,String> initParams = new HashMap<>();

initParams.put("loginUsername","admin");
initParams.put("loginPassword","123456");
initParams.put("allow","");//默认就是允许所有访问
initParams.put("deny","192.168.15.21"); //拒绝192.168.15.21访问

bean.setInitParameters(initParams);
return bean;
}


//2、配置一个web监控的filter
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());

Map<String,String> initParams = new HashMap<>();
initParams.put("exclusions","*.js,*.css,/druid/*");

bean.setInitParameters(initParams);

bean.setUrlPatterns(Arrays.asList("/*"));

return bean;
}
}

这个类除了创建数据源到spring容器外,还配置了Druid的监控web页面(可选)。

(4)测试数据库连接

ApplicationTests.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.qcmoke;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

/**
* druid数据库连接池配置类
* 如果不配置,那么默认连接池为tomcat.jdbc.pool.DataSource
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
@Autowired
DataSource dataSource;

@Test
public void contextLoads() throws SQLException {
System.out.println(dataSource);//查看数据源dataSource
Connection connection = dataSource.getConnection();
System.out.println(connection!=null?"conn success":"conn fail");
connection.close();

}
}

2. 整合MyBatis

(1)数据表

resources/sql/mybatis.sql

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for department
-- ----------------------------
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`departmentName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for employee
-- ----------------------------
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`lastName` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`email` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
`gender` int(2) NULL DEFAULT NULL,
`d_id` int(11) NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of employee
-- ----------------------------
INSERT INTO `employee` VALUES (1, 'wen', '166@qq.com', 1, 1);

SET FOREIGN_KEY_CHECKS = 1;

(2)pojo

Employee.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
package com.qcmoke.bean;

public class Employee {

private Integer id;
private String lastName;
private Integer gender;
private String email;
private Integer dId;

public void setId(Integer id) {
this.id = id;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public void setGender(Integer gender) {
this.gender = gender;
}

public void setEmail(String email) {
this.email = email;
}

public void setdId(Integer dId) {
this.dId = dId;
}

public Integer getId() {
return id;
}

public String getLastName() {
return lastName;
}

public Integer getGender() {
return gender;
}

public String getEmail() {
return email;
}

public Integer getdId() {
return dId;
}

@Override
public String toString() {
return "Employee{" +
"id=" + id +
", lastName='" + lastName + '\'' +
", gender=" + gender +
", email='" + email + '\'' +
", dId=" + dId +
'}';
}
}

Department.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.qcmoke.bean;

public class Department {

private Integer id;
private String departmentName;

public void setId(Integer id) {
this.id = id;
}

public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}

public Integer getId() {
return id;
}

public String getDepartmentName() {
return departmentName;
}
}

(3)引入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!--springboot jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--引入druid-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>

完整依赖

pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.qcmoke</groupId>
<artifactId>spring_boot_04_mybatis</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring_boot_04_mybatis</name>
<description>Demo project for Spring Boot</description>

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

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>

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

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


<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<!--springboot jdbc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--引入druid-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>


<dependency>
<groupId>net.minidev</groupId>
<artifactId>json-smart</artifactId>
<version>2.2.1</version>
</dependency>
</dependencies>

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

</project>

(4)配置mybatis

方式一:配置文件方式配置MyBatis

resources/mybatis/mybatis-config.xml

1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/> <!--开启驼峰式命名规则支持-->
</settings>
</configuration>

方式二:配置类方式配置MyBatis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package com.qcmoke.config;

import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* 配置类方式配置MyBatis
*/
@MapperScan(value = "com.qcmoke.mapper") //批量扫描,在配置类中使用@MapperScan将某个包下的所有接口扫描装配到容器中,如果不扫描可以在mapper接口上使用@Mapper 注解扫描装配到容器中
@Configuration
public class MyBatisConfig {
@Bean
public ConfigurationCustomizer configurationCustomizer(){
return new ConfigurationCustomizer(){
/**
* 开启驼峰式命名规则支持
* @param configuration
*/
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true);
}
};
}
}

(5)配置springboot和数据源

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
spring:
##配置druid连接池(数据源)
datasource:
# 数据源基本配置
username: root
password: 123456
driver-class-name: com.mysql.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/mybatis
type: com.alibaba.druid.pool.DruidDataSource
# 数据源其他配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

##配置mybatis
mybatis:
config-location: classpath:mybatis/mybatis-config.xml # 指定全局配置文件位置
mapper-locations: classpath:mybatis/mapper/*.xml # 指定sql映射文件位置

# schema:
# - classpath:sql/department.sql
# - classpath:sql/employee.sql

druid可选的属性配置(如initialSize、minIdle等),默认比不起作用(但不会报错),如果需要这些可选配置需要new DruidDataSource()到spring容器中覆盖掉原来默认的数据源。并通过@ConfigurationProperties(prefix = "spring.datasource")的方式将datasource的属性包括可选注入到这个新建的实例对象中。

src/main/java/com/qcmoke/config/DruidConfig.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
package com.qcmoke.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

@Configuration
public class DruidConfig {

/**
* 创建数据源
*/
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid(){
return new DruidDataSource();
}

/**
* 配置Druid的监控web页面
* druid监控地址http://localhost:8080/druid/login.html
* @return
*/

//1、配置一个管理后台的Servlet
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
Map<String,String> initParams = new HashMap<>();

initParams.put("loginUsername","admin");
initParams.put("loginPassword","123456");
initParams.put("allow","");//默认就是允许所有访问
initParams.put("deny","192.168.15.21"); //拒绝192.168.15.21访问

bean.setInitParameters(initParams);
return bean;
}


//2、配置一个web监控的filter
@Bean
public FilterRegistrationBean webStatFilter(){
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());

Map<String,String> initParams = new HashMap<>();
initParams.put("exclusions","*.js,*.css,/druid/*");

bean.setInitParameters(initParams);

bean.setUrlPatterns(Arrays.asList("/*"));

return bean;
}
}

这个类除了创建数据源到spring容器外,还配置了Druid的监控web页面(可选)。

(6)mapper

com.qcmoke.mapper.EmployeeMapper

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.qcmoke.mapper;


import com.qcmoke.bean.Employee;
import org.apache.ibatis.annotations.Mapper;

//@Mapper //使用@Mapper注解指定操作数据库的mapper接口。如果不写这个@Mapper注解,可以在配置类中使用@MapperScan将某个包下的所有接口扫描装配到容器中
public interface EmployeeMapper {

public Employee getEmpById(Integer id);

public void insertEmp(Employee employee);
}

配置mapper(配置文件版)

resources/mybatis/mapper/EmployeeMapper.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?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.qcmoke.mapper.EmployeeMapper">
<!-- public Employee getEmpById(Integer id);

public void insertEmp(Employee employee);-->
<select id="getEmpById" resultType="com.qcmoke.bean.Employee">
SELECT * FROM employee WHERE id=#{id}
</select>

<insert id="insertEmp">
INSERT INTO employee(lastName,email,gender,d_id) VALUES (#{lastName},#{email},#{gender},#{dId})
</insert>
</mapper>

配置mapper(注解版)

com.qcmoke.mapper.DepartmentMapper

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.qcmoke.mapper;


import com.qcmoke.bean.Department;
import org.apache.ibatis.annotations.*;
import org.mybatis.spring.annotation.MapperScan;

@Mapper //使用@Mapper注解指定操作数据库的mapper接口。如果不写这个@Mapper注解,可以在配置类中使用@MapperScan将某个包下的所有接口扫描装配到容器中
public interface DepartmentMapper {

@Select("select * from department where id=#{id}")
public Department getDeptById(Integer id);

@Delete("delete from department where id=#{id}")
public int deleteDeptById(Integer id);

@Options(useGeneratedKeys = true,keyProperty = "id") //设置id自动增长
@Insert("insert into department(department_name) values(#{departmentName})")
public int insertDept(Department department);

@Update("update department set department_name=#{departmentName} where id=#{id}")
public int updateDept(Department department);
}

(7)controller

com.qcmoke.controller.EmpController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package com.qcmoke.controller;

import com.qcmoke.bean.Employee;
import com.qcmoke.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class EmpController {

@Autowired
EmployeeMapper employeeMapper;

@GetMapping("/emp/{id}")
public Employee getEmp(@PathVariable("id") Integer id){
return employeeMapper.getEmpById(id);
}
}

com.qcmoke.controller.DeptController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.qcmoke.controller;

import com.qcmoke.bean.Department;
import com.qcmoke.mapper.DepartmentMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class DeptController {

@Autowired
DepartmentMapper departmentMapper;

/**
* 根据id获取部门数据
* @param id
* @return
*/
@GetMapping("/dept/{id}")
public Department getDepartment(@PathVariable("id") Integer id){
return departmentMapper.getDeptById(id);
}

/**
* 新增部门
* @param department
* @return
*/
@GetMapping("/dept")
public Department insertDept(Department department){
departmentMapper.insertDept(department);
return department;
}
}

(8)测试

ApplicationTests

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package com.qcmoke;

import com.qcmoke.mapper.DepartmentMapper;
import com.qcmoke.mapper.EmployeeMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {

@Autowired
ApplicationContext ac;

@Autowired
DepartmentMapper departmentMapper;

@Autowired
EmployeeMapper employeeMapper;


@Test
public void contextLoads() {
String[] names = ac.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}

@Test
public void getemp(){
System.out.println(employeeMapper.getEmpById(1));;
}

@Test
public void getdepts(){
System.out.println(departmentMapper.getDeptById(1));
}



}

security依赖包只要引入了,它会默认给项目加入密码。用户名是user 默认密码在控制台找下。

六、整合Redis

1. 引入Redis依赖

1
2
3
4
5
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

2. 配置文件

3. 添加Redis配置类

不添加这个配置类的话,默认Redis是以jdk默认序列化的二进制方式进行存取的,这可能不太友好,所以这里有必要写一个配置类来实现Redis以json格式字符串进行自动存取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package com.qcmoke.configuration;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
* Spring Boot2.X 对Redis的专用配置类
* 参考:https://blog.csdn.net/caojidasabi/article/details/83059642
*
* @author qcmoke
* @date 2019/6/16 15:34
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

private Duration timeToLive = Duration.ZERO;

public void setTimeToLive(Duration timeToLive) {
this.timeToLive = timeToLive;
}

/**
* RedisTemplate配置
*
* @param factory RedisConnectionFactory
* @return RedisTemplate<String, Object>
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<String, Object>();
template.setConnectionFactory(factory);
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
template.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
template.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用jackson
template.setValueSerializer(getJackson2JsonRedisSerializer());
// hash的value序列化方式采用jackson
template.setHashValueSerializer(getJackson2JsonRedisSerializer());
template.afterPropertiesSet();
return template;
}

/**
* 获取Jackson2JsonRedisSerializer
* 序列化策略
*/
private Jackson2JsonRedisSerializer<Object> getJackson2JsonRedisSerializer() {
Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
ObjectMapper om = new ObjectMapper();
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
jackson2JsonRedisSerializer.setObjectMapper(om);
return jackson2JsonRedisSerializer;
}

/***********注解时才需要以下配置,不用注解的话以上配置即可****************/

@Bean
public CacheManager cacheManager(RedisConnectionFactory factory) {
RedisSerializer<String> redisSerializer = new StringRedisSerializer();
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() //生成一个默认配置,通过config对象即可对缓存进行自定义配置
.entryTtl(timeToLive)//设置缓存的默认过期时间,也是使用Duration设置
.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(getJackson2JsonRedisSerializer()))
.disableCachingNullValues();//不缓存空值

return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}

@Override
@Bean
public KeyGenerator keyGenerator() {
return (target, method, params) -> {
StringBuilder sb = new StringBuilder();
sb.append(target.getClass().getName());
sb.append(method.getName());
for (Object obj : params) {
sb.append(obj.toString());
}
return sb.toString();
};
}
}

4. 使用

思路:查询的时候先查看redis缓存中有没有,有的话则直接从redis中获取数据返回,不经过dao层访问mysql了,如果redis中没有相应的缓存才调用dao层访问mysql。而进行删除的时候要把redis中的相应缓存删除掉,进行修改时也要修改相应的缓存。

4.1 dao

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.qcmoke.dao;


import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qcmoke.entity.Student;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;

import java.util.List;

@Mapper
@Repository
public interface StudentMapper extends BaseMapper<Student> {
@Delete("delete from student where sid=#{0}")
Boolean deleteBySid(Integer sid);

@Select("select * from student where sid = #{0}")
Student selectOneBySid(Integer sid);

}

4.2 service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.qcmoke.service;

import com.qcmoke.entity.Student;

import java.util.List;

public interface IStudentService {
Student getOne(Integer sid);

Student update(Student student);

Boolean delete(Integer sid);

Integer add(Student student);

List<Student> getList();
}
  • spring注解的方式进行缓存管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
package com.qcmoke.service;

import com.qcmoke.dao.StudentMapper;
import com.qcmoke.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
* TODO
*
* @author qcmoke
* @date 2019/6/16 2:57
*/
@Service("iStudentServiceImpl")
public class IStudentServiceImpl implements IStudentService {

@Autowired
StudentMapper studentMapper;

@Cacheable(cacheNames = "student", key = "#sid", unless = "#result == null")
@Override
public Student getOne(Integer sid) {
return studentMapper.selectOneBySid(sid);
}

@Transactional
@CachePut(cacheNames = "student", key = "#student.sid")
@Override
public Student update(Student student) {
studentMapper.updateById(student);
return student;
}

@Transactional
@CacheEvict(cacheNames = "student", key = "#sid")
@Override
public Boolean delete(Integer sid) {
return studentMapper.deleteById(sid) > 0;
}

@Transactional
@Override
public Integer add(Student student) {
return studentMapper.insert(student);
}


@Override
public List<Student> getList() {
return null;
}
}
  • 使用RedisTemplate进行缓存管理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package com.qcmoke.service;

import com.qcmoke.dao.StudentMapper;
import com.qcmoke.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service(value = "iStudentServiceImpl2")
public class IStudentServiceImpl2 implements IStudentService {
@Autowired
StudentMapper studentMapper;

/**
* 操作对象(key和value都是对象)
*/
@Autowired
RedisTemplate redisTemplate;


@Override
public Student getOne(Integer sid) {
Student student = (Student)redisTemplate.opsForValue().get("student_"+sid);
if (student == null) {
student = studentMapper.selectOneBySid(sid);
if (student!=null){
redisTemplate.opsForValue().set("student_"+sid, student);
}
}
return student;
}

@Transactional
@Override
public Student update(Student student) {
int flag = studentMapper.updateById(student);
if (flag>0){
redisTemplate.opsForValue().set("student_"+student.getSid(), student);
}
return student;
}

@Transactional
@Override
public Boolean delete(Integer sid) {
boolean flag1 = studentMapper.deleteById(sid) > 0;

Boolean flag2 = false;
if (flag1){
flag2= redisTemplate.delete("student_" + sid);
}
return flag1 && flag2;
}

@Transactional
@Override
public Integer add(Student student) {
return studentMapper.insert(student);
}


@Override
public List<Student> getList() {
return null;
}
}

复杂的缓存注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
package com.qcmoke.service;

import com.qcmoke.dao.StudentMapper;
import com.qcmoke.entity.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;


@CacheConfig(cacheNames = "student") //抽取缓存的公共配置
@Service("iStudentServiceImpl3")
public class IStudentServiceImpl3 implements IStudentService {

@Autowired
StudentMapper studentMapper;


@Cacheable(key = "#sid")
@Override
public Student getOne(Integer sid) {
return studentMapper.selectOneBySid(sid);
}


@Cacheable(key = "#root.method.name")
@Override
public List<Student> getList() {
return studentMapper.selectList(null);
}

@Transactional
@Caching(
put = {
@CachePut(key = "#student.sid")
},
evict = {
@CacheEvict(key = "'getList'")
}
)
@Override
public Student update(Student student) {
studentMapper.updateById(student);
return student;
}

@Transactional
@Caching(
evict = {
@CacheEvict(key = "#sid"),
@CacheEvict(key = "'getList'")
}
)
@Override
public Boolean delete(Integer sid) {
return studentMapper.deleteById(sid) > 0;
}

@Transactional
@Caching(
put = {
@CachePut(key = "#student.sid")
},
evict = {
@CacheEvict(key = "'getList'")
}
)
@Override
public Integer add(Student student) {
return studentMapper.insert(student);
}
}

4.3 test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package com.qcmoke;

import com.qcmoke.entity.Student;
import com.qcmoke.service.IStudentService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;
import java.util.List;


/**
* 一、快速体验缓存
* 步骤:
* 1、在配置类开启基于注解的缓存 @EnableCaching
* 2、标注缓存注解即可
* 3、默认springboot自带的SimpleCacheConfiguration使用的是ConcurrentMapCacheManager==ConcurrentMapCache;将数据保存在ConcurrentMap<Object, Object>中。但开发中却常常使用的是缓存中间件;redis、memcached、ehcache;
*
* 二、整合redis作为缓存,它是内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。)
* 1、安装redis
* 2、引入redis的starter依赖
* 3、配置redis
* 4、测试缓存
* 原理:容器中保存的SimpleCacheConfiguration会被替换成RedisCacheConfiguration;缓存管理器ConcurrentMapCacheManager会被替换成RedisCacheManager;
* 1)、引入redis的starter
* 2)、RedisCacheManager 帮我们创建 RedisCache 来作为缓存组件;而RedisCache就是通过操作redis来缓存数据的
* 3)、默认使用的是 RedisTemplate<Object, Object>,所有存数据 k-v 都是Object,故默认是利用jdk的序列化机制来保存数据到Redis的。如果希望保存为json字符串,可以通过配置类进行配置
* 4)、自定义CacheManager;
* 5)、使用注解实现缓存
* @Cacheable(缓存)
* 1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字和key获取;
* 2、没有查到缓存就调用目标方法;
* 3.将目标方法返回的结果,放进缓存中
* * 几个属性:
* * cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存;
* *
* * key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值 1-方法的返回值
* * 编写SpEL; #i d;参数id的值 #a0 #p0 #root.args[0]
* * getEmp[2]
* *
* * keyGenerator:key的生成器;可以自己指定key的生成器的组件id
* * key/keyGenerator:二选一使用;
* *
* * cacheManager:指定缓存管理器;或者cacheResolver指定获取解析器
* *
* * condition:指定符合条件的情况下才缓存;
* * ,condition = "#id>0"
* * condition = "#a0>1":第一个参数的值》1的时候才进行缓存
* *
* * unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断
* * unless = "#result == null"
* * unless = "#a0==2":如果第一个参数的值是2,结果不缓存;
* * sync:是否使用异步模式
*
* @CacheEvict(缓存清除)
* * key:指定要清除的数据
* * allEntries = true:指定清除这个缓存中所有的数据
* * beforeInvocation = false:缓存的清除是否在方法之前执行
* * 默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除
* * beforeInvocation = true:
* * 代表清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除
*
* @CachePut(更新缓存)
* 既调用方法,又更新缓存数据;同步更新缓存
* * 修改了数据库的某个数据,同时更新缓存;
* * 运行时机:
* * 1、先调用目标方法
* * 2、将目标方法的结果缓存起来
* *
*/

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestRedisCache {
@Resource(name = "iStudentServiceImpl")
private IStudentService studentService;
@Test
public void getOne(){
System.out.println(studentService.getOne(1));
}
@Test
public void update(){
Student student = studentService.getOne(1);
student.setSname("xiaolifeidao");
System.out.println(studentService.update(student));
}
@Test
public void delete(){
System.out.println(studentService.delete(1));
}




@Resource(name = "iStudentServiceImpl2")
private IStudentService iStudentServiceImpl2;
@Test
public void getOne2(){
System.out.println(iStudentServiceImpl2.getOne(2));
}
@Test
public void update2(){
Student student = iStudentServiceImpl2.getOne(2);
student.setSname("zh");
System.out.println(iStudentServiceImpl2.update(student));
}
@Test
public void delete2(){
System.out.println(iStudentServiceImpl2.delete(2));
}




@Resource(name = "iStudentServiceImpl3")
private IStudentService iStudentServiceImpl3;

@Test
public void getOne3(){
System.out.println(iStudentServiceImpl3.getOne(2));
}
@Test
public void update3(){
Student student = iStudentServiceImpl3.getOne(2);
student.setSname("lisi");
System.out.println(iStudentServiceImpl3.update(student));
}
@Test
public void getList(){
List<Student> list = iStudentServiceImpl3.getList();
System.out.println(list);
}
@Test
public void delete3(){
System.out.println(iStudentServiceImpl3.delete(3));
}
}

七、任务调度

1.定时任务

开发中经常需要执行一些定时任务,比如需要在每天凌晨时候,分析一次前一天的日志信息。Spring为我们提供了异步执行任务调度的方式。

两个重要的注解:@EnableScheduling、@Scheduled(cron = “cron表达式”)

cron表达式:

字段 允许值 允许的特殊字符
0-59 , - * /
0-59 , - * /
小时 0-23 , - * /
日期 1-31 , - * ? / L W C
月份 1-12 , - * /
星期 0-7或SUN-SAT 0,7是SUN , - * ? / L C #
特殊字符 代表含义
, 枚举
- 区间
* 任意
/ 步长
? 日/星期冲突匹配
L 最后
W 工作日
C 和calendar联系后计算过的值
# 星期,4#2,第2个星期四

1.1 配置类

1
2
3
4
5
6
7
8
9
10
package com.qcmoke.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;


@EnableScheduling //开启基于注解的定时任务
@Configuration
public class ScheduledConfig {
}

1.2 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package com.qcmoke.service;


import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

@Service
public class ScheduledService {

/**
* second(秒), minute(分), hour(时), day of month(日), month(月), day of week(周几).
* 0 * * * * MON-FRI
* 【0 0/5 14,18 * * ?】 每天14点整,和18点整,每隔5分钟执行一次
* 【0 15 10 ? * 1-6】 每个月的周一至周六10:15分执行一次
* 【0 0 2 ? * 6L】每个月的最后一个周六凌晨2点执行一次
* 【0 0 2 LW * ?】每个月的最后一个工作日凌晨2点执行一次
* 【0 0 2-4 ? * 1#1】每个月的第一个周一凌晨2点到4点期间,每个整点都执行一次;
*/
// @Scheduled(cron = "0 * * * * MON-SAT")
//@Scheduled(cron = "0,1,2,3,4 * * * * MON-SAT")
// @Scheduled(cron = "0-4 * * * * MON-SAT")
@Scheduled(cron = "0/4 * * * * MON-SAT") //每4秒执行一次
public void hello(){
System.out.println("hello ... ");
}
}

在启动springboot应用后,每隔4秒钟就会打印"hello ..."

2. 异步任务

2.1 配置类

1
2
3
4
5
6
7
8
9
package com.qcmoke.configuration;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;

@Configuration
@EnableAsync //开启异步注解功能
public class AsyncConfig {
}

2.2 定义异步方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package com.qcmoke.service;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class AsyncService {

//告诉Spring这是一个异步方法
@Async
public void async(){
try {
Thread.sleep(10000);
System.out.println("处理异步数据中...");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

2.3 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package com.qcmoke.controller;

import com.qcmoke.service.AsyncService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class AsyncController {
@Autowired
AsyncService asyncService;

@ResponseBody
@GetMapping("/testAsync")
public String async(){
/**
* ①和②是两条执行语句,由于①方法使用了@Async注解,所以①和②是异步进行的,也就是在这个方法体内①开辟了一条新的线程。②不需要等待①同步执行结束才执行
*/
asyncService.async();//① 调用异步方法,需要10秒执行完
return "执行下一条语句";//②
}
}

八. 邮件开发

邮件任务是其实是一个客户端收发邮件的过程,这里以QQ邮箱为例,首先需要在项目中登录到配置好的QQ邮件服务器后,发送邮件给QQ邮件服务器,QQ邮服务器在中转你发送的邮件给接收邮件账号对应的邮件服务器,最后再通过该服务器发送给邮件接收者的账号。所以说只要你本地的主机可以连网那么就可以完成这个过程。

1560849820929

1. 引入依赖

1
2
3
4
5
<!-- springboot mail -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>

2. 配置

1
2
3
4
5
spring:
mail:
username: 1667164190@qq.com
password: fi*********gih
host: smtp.qq.com

注意:这里的password不是邮箱登录的密码,而是邮件服务器对指定邮件账号的登录授权码,对于QQ邮箱来说,授权码的获取可参考下图:

1560848386225

3. 测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package com.qcmoke;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.test.context.junit4.SpringRunner;

import javax.mail.MessagingException;
import javax.mail.internet.MimeMessage;
import java.io.File;

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestMail {
@Autowired
JavaMailSenderImpl mailSender;

/**
* 发送简单邮件
*/
@Test
public void sendMail(){
SimpleMailMessage message = new SimpleMailMessage();

message.setSubject("通知-今晚开会");//设置邮件标题
message.setText("今晚7:30开会");//设置邮件内容

message.setTo("qcmoke@gmail.com");//设置收邮件的账号
message.setFrom("1667164190@qq.com");//设置发邮件的账号
mailSender.send(message);
}

/**
* 发送复杂邮件
*/
@Test
public void sendMail2() throws MessagingException {
//1、创建一个复杂的消息邮件
MimeMessage mimeMessage = mailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);

helper.setSubject("通知-今晚开会");//设置邮件标题
helper.setText("<b style='color:red'>今天 7:30 开会</b>",true);//设置邮件内容

helper.setTo("qcmoke@gmail.com");//设置收邮件的账号
helper.setFrom("1667164190@qq.com");//设置发邮件的账号

//添加附件文件
helper.addAttachment("1.jpg",new File("src/main/resources/static/img/1.jpg"));
helper.addAttachment("2.jpg",new File("src/main/resources/static/img/2.png"));

mailSender.send(mimeMessage);

}

}

九、热部署

添加依赖

1
2
3
4
5
6
 <!--热部署依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional><!-- optional=true,依赖不会传递,即子项目如果依赖当前项目,不会获取devtools,那么需要重新引入devtools -->
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
<build>
<plugins>
<!-- 这个插件,可以将应用打包成一个可执行的jar包;-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<fork>true</fork> <!--fork : 如果没有该项配置,肯定devtools不会起作用,即应用不会restart -->
</configuration>
</plugin>
</plugins>
</build>

添加以上依赖后,在eclipse中已经可以实现热启动,但是在idea中还需要做两处设置。

(1)setting –> compiler ,将 Build project automatically 勾选上

(2)快捷键ctrl + shift + alt + /或者Ctrl+ shift + a搜索 registry后选第一个,弹出框后下拉找到compiler.automake.allow.when.app.running 勾选上即可。

十 深入底层

1
2
3
4
5
6
7
8
9
10
BeanPostProcessor
https://www.jianshu.com/p/e6a6013ef0a0

ApplicationListener
https://my.oschina.net/u/2931319/blog/1631740

http://followtry.cn/2019-10-30/spring-custom-scanner-demo.html

ApplicationListener<ContextRefreshedEvent>
https://www.shangmayuan.com/a/b9ea85ed208b46ac8c7c64b2.html

待续…



----------- 本文结束 -----------




如果你觉得我的文章对你有帮助,你可以打赏我哦~
0%