Redis

Redis介绍

一个开源(BSD许可)的、内存中的数据结构存储系统,可用作数据库、缓存消息中间件,并提供多种语言的API,是一个高性能的key-value数据库。

与其他key-value缓存产品相比有以下三个特点:

  1. 支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用
  2. 不仅仅支持简单的key-value类型的数据,同时还提供list、set、zset、hash等数据结构的存储
  3. 支持数据的备份,即master-slave模式的数据备份

优点

  1. 存取速度快:Redis速度非常快,每秒可执行大约110000次的设值操作,或者执行81000次的读取操作

  2. 支持丰富的数据类型:Redis支持开发人员常用的大多数数据类型,例如列表、集合、排序集和散列等

  3. 操作具有原子性:所有Redis操作都是原子操作,这确保如果两个客户端并发访问,Redis服务器能接收更新后的值

  4. 提供多种功能:Redis提供了多种功能特性,可用作非关系型数据库、缓存中间件、消息中间件等

Redis安装与配置

1. 安装软件

安装Redis服务端,这里蓝奏云的版本是5.0.10版本,如果你需要最新版的可以去官方仓库下载

Redis可视化界面

Redis有两款可视化操作软件,根据自己的喜好来选择,到目前为止,RedisInsight-v2是Redis开源免费的版本,而RedisDesktopManager已经开始收费了,但下面这个版本还是免费版。

2. 配置Windows服务

配置Windows服务是为了之后使用Redis更加方便,不用每次需要用到Redis都要手动打开Redis服务,配置Windows之后,如果有服务需要用到Redis,Redis就会自行自动,方便本地开发。

打开Redis服务端的安装目录(如我安装redis在E:\studyAPP\Redis-3.2.100),打开命令行,执行以下命令,表示将redis-server服务添加到Windows服务中,这样以后使用不需要再手动启动redis-server了。

1
redis-server --service-install redis.windows-service.conf --loglevel verbose

3. 测试连接

在命令行进入redis-cli客户端,看看能不能正常访问,如果出现拒绝访问,说明redis-server没有打开,我首次打开redis-cli就是访问失败,其实就是redis-server没有启动,需要去Windows服务手动打开一次,之后就不需要手动打开了。

此电脑-->管理-->服务和应用程序-->服务-->找到Redis-->右键属性-->启动类型改为自动-->自动服务

01-Redis 的属性(本地计算机)20230519-453

重新在命令行打开redis-cli

02-管理员 Windows PowerSh20230519-585

4. 客户端

RedisDesktopManager

redis默认是没有密码的,如果没有修改本机的配置,默认只允许本机访问,不需要其他机器远程访问,所以不需要密码是可以连接的。

03-新连接设置20230519-013

连接到redis后,可以看到下面有16个数据库。

04-Redis Desktop Manage20230519-615

RedisInsight-v2

连接方式也差不多,点击添加数据库然后直接点连接就可以了。

05-My Redis databases20230519-669

Spring Data Redis

前言

Spring Data Redis

Spring Data Redis是Spring Framework的一个模块,它提供了对Redis数据库的整合和支持。Spring Data Redis基于Jedis和Lettuce客户端库,提供了Redis连接、数据操作和事务管理等功能,简化了Redis与Spring应用程序的集成。

RedisTemplate

RedisTemplate是Spring Data Redis中的核心组件之一,它是对Jedis和Lettuce客户端库的进一步封装,提供了对Redis操作的更高级别的抽象。RedisTemplate实现了Spring的高级数据访问接口(如RedisOperations、ListOperations、ValueOperations等),并提供了线程安全的Redis连接等功能。

Spring Data Redis是基于RedisTemplate的,可以通过RedisTemplate直接访问Redis数据库。除此之外,Spring Data Redis还提供了更多的特性和功能,如:

  1. 对Redis的数据结构(如Hashes、Sets、Zsets等)提供更加便捷的操作方法;
  2. 提供了对分布式锁和Lua脚本的支持;
  3. 支持Redis的发布订阅模式,可以方便地实现消息队列等功能;
  4. 支持Redis的集群模式,可以方便地横向扩展应用。

总的来说,Spring Data Redis提供了更加全面、高级的Redis集成功能和抽象,而RedisTemplate是Spring Data Redis的基础组件之一,提供了对Redis操作的高级别抽象和线程安全的连接管理。

配置Redis数据库

密码如果在安装数据库没有设置的话,这里不需要填,如果有修改过密码则填入对应密码。

1
2
3
4
5
6
7
8
9
10
11
12
spring:
redis:
port: 6379 # Redis服务器连接端口,默认是6379
host: localhost # Redis服务器地址
password: # Redis服务器连接密码(默认为空)
database: 0 # Redis数据库索引(默认为0)
lettuce:
pool:
max-idle: 10 # 连接池中的最大空闲连接
min-idle: 0 # 连接池中的最小空闲连接
max-active: 200 # 连接池最大连接数(使用负值表示没有限制)
max-wait: -1 # 连接池最大阻塞等待时间(使用负值表示没有限制)

编写实体类

编写一个实体类,用来将整个实体类的数据存储到Redis中。

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
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.index.Indexed;

@RedisHash("persons") // 指定操作实体类对象在Redis数据库中的存储空间
public class Person {

@Id // 标识实体类主键
private String id;

@Indexed // 标识对应属性在Redis数据库中生成二级索引
private String firstname;

@Indexed
private String lastname;

public Person() {
}

public Person(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}
// 自行添加 set get toString方法
}

继承CrudRepository类

1
2
3
4
5
import com.itheima.domain.Person;
import org.springframework.data.repository.CrudRepository;
public interface PersonRepository extends CrudRepository<Person, String> {

}

测试方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import site.hikki.domain.Person;
import site.hikki.repository.PersonRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class RedisTests {

@Autowired
private PersonRepository repository;

@Test
public void savePerson() {
Person person =new Person("小码","同学");
// 向Redis数据库添加数据
Person save = repository.save(person);
System.out.println(save);
}
}

查看结果

06-Redis Desktop Manage20230519-277

RedisTemplate

RedisTemplate和JdbcTemplate差不多,都是Spring提供的框架,后面的学习中,你会发现,其他还有其他的类似XxxTemplate框架,这些都是有Spring提供的框架,SpringBoot将这些框架整合起来了,提高开发者效率,开发者需要那套功能就直接使用。

添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!--        redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- web启动依赖,使用redis进行存储会用到jackson解析依赖,web启动依赖就有 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

反序列化操作需要jackson依赖,而spring-boot-starter-web启动依赖有jackson依赖,如果你不需要进行反序列化操作可以不用spring-boot-starter-web

如果你设置了反序列化操作而不添加jackson依赖,就会报如下错误:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.data.redis.core.RedisTemplate]: Factory method 'redisTemplate' threw exception; nested exception is java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/JsonSerializer
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
... 102 more
Caused by: java.lang.NoClassDefFoundError: com/fasterxml/jackson/databind/JsonSerializer
at site.hikki.template.RedisConfig.redisTemplate(RedisConfig.java:24)
at site.hikki.template.RedisConfig$$EnhancerBySpringCGLIB$$82f29239.CGLIB$redisTemplate$0(<generated>)
at site.hikki.template.RedisConfig$$EnhancerBySpringCGLIB$$82f29239$$FastClassBySpringCGLIB$$1ab67068.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
at site.hikki.template.RedisConfig$$EnhancerBySpringCGLIB$$82f29239.redisTemplate(<generated>)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
... 103 more
Caused by: java.lang.ClassNotFoundException: com.fasterxml.jackson.databind.JsonSerializer
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:520)
... 114 more

配置Redis

在配置项application.yml配置文件中添加如下配置,redis刚安装时是默认没有密码的,值允许本机使用,不允许远程本地的redis,所以不设置密码也是安全的,如果你要远程redis,则建议设置一下密码较好。

1
2
3
4
5
6
7
8
9
10
11
12
13
spring:
redis:
port: 6379 # Redis服务器连接端口,默认是6379
host: localhost # Redis服务器地址
password: # Redis服务器连接密码(默认为空)
database: 0 # Redis数据库索引(默认为0)
lettuce: # 连接池
shutdown-timeout: 100ms
pool:
max-wait: PT10S # 连接池最大阻塞等待时间(使用负值表示没有限制)
max-active: 100 # 连接池最大连接数(使用负值表示没有限制)
min-idle: 1 # 连接池中的最小空闲连接
max-idle: 30 # 连接池中的最大空闲连接

配置反序列化

我们正常在Java中对redis进行添加,都是被序列化过的,由ObjectOutputStream和ObjectInputStream类来实现,序列化的目的是在进行网络传输或者本地存储时保持数据完整性。

Spring Data Redis提供的RedisTemplate类来完成序列化和反序列化的操作,并且RedisTemplate封装了Redis的Java客户端,我们可以通过实现RedisSerializer接口并重写serializedeserialize方法,然后在RedisTemplate中使用setKeySerializersetValueSerializer方法设置自定义的序列化器。

创建一个RedisConfig类,将该类添加@Configuration注解,表示该类是一个配置类。

并且在实现RedisSerializer接口时,记得将该方法添加@Bean注解,表示将该方法收Ioc容器管理,不然Redis的反序列化怎么被实现呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {

@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<String, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
StringRedisSerializer stringRedisSerializer=new StringRedisSerializer();
GenericJackson2JsonRedisSerializer genericJackson2JsonRedisSerializer=new GenericJackson2JsonRedisSerializer();
template.setKeySerializer(stringRedisSerializer);
template.setValueSerializer(genericJackson2JsonRedisSerializer);
template.setHashKeySerializer(stringRedisSerializer);
template.setHashValueSerializer(genericJackson2JsonRedisSerializer);
return template;
}
}

编写Redis方法

新建一个RedisService类,用于封装我们常用的Redis方法,后面使用直接调用即可。

新建RedisService类后,我们需要注入RedisTemplate实现类,我们打开RedisTemplate类看看。

1
2
public class RedisTemplate<K, V> extends RedisAccessor implements RedisOperations<K, V>, BeanClassLoaderAware {
}

可以看到,RedisTemplate继承了RedisAccessor,实现了RedisOperations和BeanClassLoaderAware接口,其中我们常用对Redis操作方法都在RedisOperations<K, V>接口里面。

08-继承的方法-springboot – RedisTe20230521-579

我们对上面这些常用的方法对一个简单的封装。

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
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.concurrent.TimeUnit;

@Component
public class RedisService {
@Autowired
private RedisTemplate redisTemplate;

/**
* 判断key是否存在
*/
public boolean hasKey(String key){
if (StringUtils.hasLength(key)){
return redisTemplate.hasKey(key);
}
return false;
}

/**
* 删除key
*/
public boolean delKey(String key){
if (StringUtils.hasLength(key)){
return redisTemplate.delete(key);
}
return false;
}

/**
* 设置key过期时间
* 形参timeUnit常见单位如下:
* TimeUnit.DAYS 天
* TimeUnit.HOURS 小时
* TimeUnit.MINUTES 分钟
* TimeUnit.SECONDS 秒
*/
public boolean expTime(String key, long timeout, TimeUnit timeUnit){
if (StringUtils.hasLength(key)){
return redisTemplate.expire(key,timeout,timeUnit);
}
return false;
}


/**
* 设置 -Set类型key
*/
public <T> boolean set(String key,T value){
if (StringUtils.hasLength(key)){
redisTemplate.opsForValue().set(key,value);
return true;
}
return false;
}

/**
* get -Set 类型key
*/
public <T> T get(String key){
ValueOperations<String,T> valueOperations = redisTemplate.opsForValue();
return valueOperations.get(key);
}

// -----------------------
/**
* 添加缓存
* @param key
* @param value
* @return
*/
public boolean redisCache(String key,Object value){
if (hasKey(key)){
return true;
}else {
return set(key,value);
}
}
}

添加实体类

用户后面对实体类测试

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
import org.springframework.data.annotation.Id;
import org.springframework.data.redis.core.RedisHash;
import org.springframework.data.redis.core.index.Indexed;

@RedisHash("persons") // 指定操作实体类对象在Redis数据库中的存储空间
public class Person {

@Id // 标识实体类主键
private String id;

@Indexed // 标识对应属性在Redis数据库中生成二级索引
private String firstname;

@Indexed
private String lastname;

public Person() {
}

public Person(String firstname, String lastname) {
this.firstname = firstname;
this.lastname = lastname;
}

public String getId() {
return id;
}

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

public String getFirstname() {
return firstname;
}

public void setFirstname(String firstname) {
this.firstname = firstname;
}

public String getLastname() {
return lastname;
}

public void setLastname(String lastname) {
this.lastname = lastname;
}

@Override
public String toString() {
return "Person{" +
"id='" + id + '\'' +
", firstname='" + firstname + '\'' +
", lastname='" + lastname + '\'' +
'}';
}
}

测试Redis

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
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import site.hikki.pojo.Person;
import site.hikki.template.RedisService;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

@SpringBootTest(classes = Application.class)
public class RedisTemplateTest {

@Resource
RedisService redisService;

/**
* 添加key
*/
@Test
public void addKey(){
String key ="test";
boolean b = redisService.set(key, "this is value");
System.out.println(b);
}

/**
* 获取key
*/
@Test
public void getKey(){
String test = redisService.get("test");
System.out.println(test);
}


/***
* 删除一个key
*/
@Test
public void delKey(){
String key = "test2";
boolean b = redisService.delKey(key);
System.out.println(b);
}


/**
* 设置Key并设置过期时间
*/
@Test
public void setKeyAndTime(){
String key = "test2";
boolean test2 = redisService.set(key, "this is test2");
// 给 test2的key设置过期时间为1分钟,一分钟后自动销毁test2
boolean b = redisService.expTime(key, 1, TimeUnit.MINUTES);
System.out.println("set key:"+test2);
System.out.println("set time:"+b);
}

/**
* 设置实体类类型的key
*/
@Test
public void setEntity(){
String key = "test3";
Person person = new Person();
person.setFirstname("小黑子");
person.setId("1");
person.setLastname("小白子");
boolean b = redisService.set(key, person);
System.out.println(b);
}

/**
* 获取实体类的key
*/
@Test
public void getEntity(){
String key = "test3";
Person person = redisService.get(key);
System.out.println(person.toString());
}
}

总的来说,RedisTemplates框架没也封装了非常多的方法,上面只是列举出常用的几个方法,你若还有其他的需求,你可以根据自己的需求来调用其他方法。

总结

RedisRepository API

它是在 Spring Data Redis 基础上封装了一层,提供了类似 JPA 的操作方式,可以通过继承 RedisRepository 接口快速实现基本的 CRUD 操作。同时,RedisRepository 还提供了诸如分页查询、按照属性名查找等常用功能。RedisRepository API 更适用于基础的 CRUD 操作和简单查询的场景。

RedisTemplate API

它是 Spring 对 Redis 的原生封装,提供了一系列操作 Redis 的方法,比如 PUT、GET、DEL、INCR 等。RedisTemplate API 相对来说操作更加灵活,支持 Redis 的所有数据结构和命令。RedisTemplate API 更适用于需要进行复杂操作的场景。

但需要注意的是,在使用 RedisTemplate API 时需要手动管理 Redis 的连接、事务,而 RedisRepository 则把这些过程封装了起来,使用起来更加方便。

MongoDB

前言

MongoDB是一个开源、高性能、无模式的文档型数据库,它是NoSQL数据库产品中的一种,是最像关系型数据库的非关系型数据库。

解决了什么

为什么出现MongoDB?

一个技术的出现,肯定是有原因的,这个技术是为了解决什么问题而出现,这才是我们关注的重点。

传统的关系型数据库在海量数据和高并发读写方面存在瓶颈问题,不能够很好的支持高性能查询。最常见的应用场景:

  • 淘宝用户数据
    • 存储位置:数据库
    • 特征:永久性存储,修改频度极低
  • 游戏装备数据、游戏道具数据
    • 存储位置:数据库、Mongodb
    • 特征:永久性存储与临时存储相结合、修改频度较高
  • 直播数据、打赏数据、粉丝数据
    • 存储位置:数据库、Mongodb
    • 特征:永久性存储与临时存储相结合,修改频度极高
  • 物联网数据
    • 存储位置:Mongodb
    • 特征:临时存储,修改频度飞速

这些场景都是存在海量的数据、结构复杂、并且对数据库频繁读写的要求,传统的关系型数据库有点吃力了,不能够很好的支持,MongoDB的出现,可以很好解决这些问题,读写速度快,支持高性能查询和聚合操作,可以总结为以下三点:

  1. 面向文档型数据模型 MongoDB采用面向文档的数据模型,可以将复杂的数据结构以嵌套文档和数组的形式保存在一个文档中,极大地简化了应用程序的数据处理过程,降低了对数据库操作的复杂度,提高了开发效率和灵活性。
  2. 分布式存储和高可用性 MongoDB支持自动分片和副本集等机制,可以轻松地实现跨多台服务器的分布式存储和高可用性,并且自带的故障转移功能和自动切换功能,保证了数据的安全性和可靠性。
  3. 高性能查询和聚合操作 MongoDB提供了强大而灵活的查询语言和聚合操作,可以快速地处理海量的数据,并支持基于索引的查询、数据聚合和MapReduce等高级功能,满足了对数据分析和数据挖掘方面的需求。

与Redis有什么不同

  1. 数据存储方式 Redis是一种基于内存的键值型数据库,数据全部存在内存中,并且可以将数据异步持久化到硬盘上。相比较而言,MongoDB是一种面向文档的数据库,数据以BSON(一种二进制的JSON格式)文档的形式存在,被组织在一个Collection中。
  2. 数据查询方式 Redis支持多种数据结构(如字符串、哈希、列表、集合、有序集合等),可以针对不同的数据结构提供不同的操作方法,如查找、排序、计数、分页、范围查询等。MongoDB支持复杂的文档型查询,可以使用内嵌文档、数组等方式来组织数据并进行查询,支持聚合操作和地理空间查询等高级功能。
  3. 数据一致性 Redis采用单线程模型,具有原子性和高并发性,但是在出现网络故障或者其他异常情况时,Redis会出现数据丢失或者数据不一致的情况。MongoDB采用多线程模型,在数据写入到主节点之后,会同步到所有的从节点上,保证数据的一致性和可靠性。
  4. 应用场景 由于Redis具有快速读写、高并发、低延迟的特点,因此适合于对数据查询频繁,但是写入操作较少的应用场景,如缓存、计数器、消息队列等。而MongoDB适用于需要海量存储、数据结构复杂、查询灵活的应用场景,如数据分析、日志存储、内容管理、社交网络等。

安装MongoDB

下载安装包

MongoDB 提供了可用于 32 位和 64 位系统的预编译二进制包,你可以从MongoDB官网下载安装,MongoDB 安装包。

下载地址:https://www.mongodb.com/try/download/community

image-20230315001624117

根据上图所示下载zip包。
提示:版本的选择:
MongoDB的版本命名规范如:x.y.z;
y为奇数时表示当前版本为开发版,如:1.5.2、4.1.13;
y为偶数时表示当前版本为稳定版,如:1.6.3、4.0.10;
z是修正版本号,数字越大越好。
详情:http://docs.mongodb.org/manual/release-notes/#release-version-numbers

安装选择Custom

image-20230315001807143

在安装过程中,在上面这步需要选择,其他都是默认下一步即可。

新建文件和文件夹

/data文件夹里面新建一个db文件夹,用于存储数据文件

image-20230315001936078

新建一个mongo.config文件,输入内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
dbpath=E:\studyAPP\MongoDB\data\db #数据库路径
# 这里是你的安装路径!!!!!

logpath=E:\studyAPP\MongoDB\log\mongod.log # 日志输出文件路径
# 这里是你的安装路径!!!!!

logappend=true #错误日志采用追加模式

journal=true #启用日志文件,默认启用

quiet=true #过滤掉无用的日志信息,若需要调试使用请设置为false

port=27017 #端口号 默认为27017

更多参数配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
systemLog:
destination: file
#The path of the log file to which mongod or mongos should send all diagnostic logging information
path: "D:/02_Server/DBServer/mongodb-win32-x86_64-2008plus-ssl-4.0.1/log/mongod.log"
logAppend: true
storage:
journal:
enabled: true
#The directory where the mongod instance stores its data.Default Value is "/data/db".
dbPath: "D:/02_Server/DBServer/mongodb-win32-x86_64-2008plus-ssl-4.0.1/data"
net:
#bindIp: 127.0.0.
port: 27017
setParameter:
enableLocalhostAuthBypass: false

安装mongo shell工具(可选)

mongo shell工具只是为了方便我们调试使用,你如果不需要再命令行中对MongoDB操作,不安装也是可以的,但还是建议安装。

下载Shell工具,打开MongoDB官网:https://www.mongodb.com/try/download/shell

image-20230315002200335

下载压缩包后,解压,将mongosh.exe文件复制到刚刚安装MongoDB/bin文件夹下,如下

image-20230315002306398

运行MongoDB服务

1
mongod --dbpath E:\studyAPP\MongoDB\data\db

image-20230315002518194

你也可以设置MongoDB的服务启动方式为延时启动,这样就不需要每次启动都要手动启动了,具体可以参考我的另一篇文章:https://blog.hikki.site/265cdd13.html

浏览器打开

http://localhost:27017/

image-20230315002556909

Shell连接(可选)

使用Shell连接前提是安装了Mongo Shell才能使用该命令。

在命令提示符输入以下shell命令即可完成登陆

1
mongosh

image-20230315002936200

查看已经有的数据库

1
2
3
show databases
# 或者
show dbs

退出mongodb

1
exit

更多参数可以通过帮助查看:

1
mongo --help

提示:MongoDB javascript shell是一个基于javascript的解释器,故是支持js程序的。

可视化操作

Studio 3T是一个MongoDB数据库管理工具,它提供了可视化操作界面、快速查询和聚合、自动完成和插件扩展等功能,使得开发人员和DBA能够更加高效地管理MongoDB数据库。

操作MongoDB数据库,使用的不是SQL语句查询,而是使用类似json获取字段这种类型来或者MongoDB数据,对json不是很熟悉的你可能有点陌生,Studio 3T就类似Navicat数据库客户端软件,可以很方便的对数据库进行操作

简单使用

Studio3T

整合MongoDB

此次试验我们使用Spring Data MongoDB数据库框架,Spring Data MongoDB是Spring Data家族的一员,提供了一套用于与MongoDB交互的API,可以轻松地实现MongoDB的CRUD操作和高级查询等功能。Spring Data MongoDB还支持异步操作、分页查询、文本搜索等高级功能,非常适合在各种规模的企业项目中使用。

除了Spring Data MongoDB,也有很多其他的MongoDB框架可供选择,如Morphia、MongoJack、ReactiveMongo等,在不同的场景下可选用不同的框架以满足项目需求。

MongoDB实现方式

Spring Data MongoDB 中默认提供了两种 API 实现方式:MongoTemplateMongoRepository

MongoTemplate API

它是 Spring 对 MongoDB 的原生封装,提供了与 MongoDB 基本操作的一一对应方法,比如插入、查找、修改、删除等。MongoTemplate API 提供了比较灵活的操作方式,适用于需要进行复杂操作的场景。

MongoRepository API

它是在 MongoTemplate API 基础上封装了一层,提供了更简洁易用的接口,支持类似 JPA 的操作方式。通过继承 MongoRepository 接口可以快速地实现基础的 CRUD 操作,同时还提供了根据属性名自动生成查询语句等功能。MongoRepository API 更适用于基础的 CRUD 操作和简单查询的场景。

实现思路

在Java中整合MongoDB,跟上面学习Redis的RedisTemplate开发是一样的步骤,只不过是换了一个数据库而已,首先还是导入依赖-->配置MongoDB数据库信息-->编写MongoDB实现类-->测试结果,步骤是差不多的,下面的例子,我会演示一个简单的评论系统,将博客评论的数据存储到MongoDB中。

评论存在用户名、邮箱、评论之间相互回复、评论者用户组等等相关信息,并且数据在前后端进行传输,肯定也是需要封装起来的。

基于上面的这个情况,我们创建几个类来封装一下信息。

  • Result实体类用来封装数据传输的基本框架
  • Result下有个data属性,该属性用来存储所有评论内容,包括子评论
  • 创建一个Comment实体类用来封装评论信息,Comment类下有个replies属性,该属性用来存储子评论的内容
  • 子评论内容使用一个SubComment实体类来封装内容

总的来说,需要三个实体类来封装内容,Result用来封装整体内容传输基本框架,Comment用来封装每一个博客评论内容,SubComment用来封装子评论的内容,封装成三层结构。

封装后大概是这样的一个数据结构:

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
{
"_id" : ObjectId("646d69a1dc3b9f57424e311b"),
"data" : [
{
"_id" : ObjectId("646d78c4ac1fa60c0d9e42"),
"nick" : "小码同学",
"avatar" : "https://blog.hikki.site/avatar.png",
"mailMd5" : "646c79358cce5479df",
"link" : "https://blog.hikki.site",
"comment" : "这博客好棒啊",
"os" : "Android",
"browser" : "Firefox/113.0",
"ipRegion" : "192.168.2.8",
"master" : true,
"like" : 5,
"replies" : [
{
"_id" : ObjectId("646d81a4f2e6764ad1e7fb50"),
"nick" : "小码同学",
"avatar" : "https://blog.hikki.site/avatar.png",
"mailMd5" : "646c8d857cce479df",
"link" : "https://blog.hikki.site",
"comment" : "这博客好棒啊",
"os" : "Android",
"browser" : "Firefox/113.0",
"ipRegion" : "192.168.2.8",
"like" : 5,
"isSpam" : false,
"created" : ISODate("2023-05-24T03:16:52.975+0000")
}
],
"more" : "",
"count" : 0,
"_class" : "site.hikki.pojo.cm.Result",
"mailMd5" : "69a1dc3b9f574"
}

配置MongoDB

导入Maven依赖

打开pom.xml配置文件导入spring-boot-starter-data-mongodb启动依赖

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

配置MongoDB信息

创建application.yml配置文件,创建工程默认配置文件是application.properties,你可以直接将properties后缀改为yml。

1
2
3
4
5
6
spring:
data:
mongodb:
port: 27017
host: localhost
database: spring_db

创建实体类

下面我以创建一个博客评论的数据为例。

Result

我们使用Result来封装每一篇文章下的评论,其中评论内容存储在data属性中,count则为data数组的长度,也就是该文章下有多少条评论数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.List;

@Data
@Document()
public class Result {

@Id
private String id; // 主键ID存储到数据库默认是ObjectId()

private List<Comment> data=null; //评论内容

private String more=null;

private Integer count=0; //data数组下条数,也就是该文章评论数量
}

Comment

在上面的类中,我们定义了一个data属性来储存评论内容,下面这个Comment就是评论的具体属性,评论中应该需要有用户名、头像、邮箱、点赞、子评论等待属性。

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
import lombok.Data;
import org.springframework.data.annotation.Id;
import java.util.Date;
import java.util.List;

/**
* 父评论 也叫根评论
*/
@Data
public class Comment {
@Id
private String id; //主键
private String nick; // 用户别名
private String avatar; // 头像URL
private String mailMd5; // 邮箱Hash
private String link; // 用户网站地址
private String comment; // 评论内容
private String os; // 评论所用系统
private String browser; // 浏览器版本
private String ipRegion; // 发布者IP地址
private boolean master;// 是否是博客这座
private Integer like=0; // 点赞数量
private List<SubComment> replies; // 子评论
private Integer top; // 置顶数量,数值越小,置顶优先权越高
private boolean isSpam=false; // 是否是垃圾邮件
private Date created=new Date(); // 创建时间

}

SubComment

在评论区中,每一条评论都可以被回复,被回复的内容就叫子评论,子评论我们使用SubComment来封装起来,子评论中不能存在子子评论,子评论之间互相回复使用rid和pid来判断识别回复用户。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import lombok.Data;
import org.springframework.data.annotation.Id;
import java.util.Date;

@Data
public class SubComment {
@Id
private String id; //主键
private String nick; // 用户别名
private String avatar; // 头像URL
private String mailMd5; // 邮箱Hash
private String link; // 用户网站地址
private String comment; // 评论内容
private String os; // 评论所用系统
private String browser; // 浏览器版本
private String ipRegion; // IP 地址
private String master;// 是否是博客这座
private Integer like; // 点赞数量
private String rid; //该评论的根父评论
private String pid; //要回复的作者ID
private String ruser; //该评论回复的是谁
private boolean isSpam=false; //该邮箱是否是垃圾邮箱
private Date created=new Date(); // 创建时间
}

接口实现

创建接口

创建一个评论管理的接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import site.hikki.pojo.cm.Comment;
import site.hikki.pojo.cm.Result;
import site.hikki.pojo.cm.SubComment;
import java.util.List;

public interface CommentDao {

Result initResult();

Object addComment(String rootId,Comment comment);

Object delComment(String rootId,String commentId);

Object updateComment(String rootId,String mailMd5, String newMailMd5);

List<Result> findAllComment();

List<Comment> findCommentById(String id);

Object addSubComment(String rootId,String commentId,SubComment subComment);

}

实现接口

显示刚刚创建的接口,使用MongoTemplate模板

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
import com.mongodb.client.result.UpdateResult;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.*;
import org.springframework.data.mongodb.core.aggregation.*;
import org.springframework.data.mongodb.core.query.*;
import org.springframework.stereotype.Repository;
import site.hikki.dao.CommentDao;
import site.hikki.pojo.cm.*;
import java.util.ArrayList;
import java.util.List;

@Repository
public class CommentDaoImpl implements CommentDao {
@Autowired
MongoTemplate mongoTemplate;

/**
* 初始化每一条博客文章
*/
@Override
public Result initResult() {
Result result = new Result();
result.setCount(0);
result.setMore("");
result.setData(new ArrayList<>());
return mongoTemplate.insert(result);
}

/**
* 添加父评论
*/
@Override
public Object addComment(String rootId,Comment comment) {
// 此处的
Query query = new Query(Criteria.where("id").is(rootId));
Update update = new Update().push("data",comment);
UpdateResult result = mongoTemplate.updateFirst(query, update, Result.class);
return result;
}

/**
*
* 删除时,如果是自带生成的_id,不需要添加 ObjectId()
* 直接使用 ObjectId 的值即可
* @param rootId 每条评论对应的文档ID
* @param commentId 父评论
* @return
*/
@Override
public Object delComment(String rootId,String commentId) {
Query query = new Query(Criteria.where("id").is(rootId));
Update update = new Update().pull("data", Query.query(Criteria.where("_id").is(commentId)));
UpdateResult updateResult = mongoTemplate.updateMulti(query, update, Result.class);
return updateResult;
}

/**
* 根据父评论Id 修改父评论邮件 Hash
* @param rootId Result根ID
* @param mailMd5 邮件原Hash值
* @param newMailMd5 需要更换后的邮件Hash值
*/
@Override
public Object updateComment(String rootId,String mailMd5, String newMailMd5) {
Query query = new Query(Criteria.where("id").is(rootId));
// 使用elemMatch操作符选择匹配的数组元素
query.addCriteria(Criteria.where("data").elemMatch(Criteria.where("mailMd5").is(mailMd5)));
// 使用set运算符更新所选数组元素的“mailMd5”字段
Update update = new Update().set("data.$.mailMd5", newMailMd5);
UpdateResult result = mongoTemplate.updateMulti(query, update, Result.class);
return result;
}


/**
* 查询整个Comment文档数据
*/
@Override
public List<Result> findAllComment() {
return mongoTemplate.findAll(Result.class);
}

/**
* 根据评论ID查询
* @param commentId 父评论ID
*/
@Override
public List<Comment> findCommentById(String commentId) {
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.unwind("data"),
Aggregation.match(Criteria.where("data._id").is(new ObjectId(commentId))),
Aggregation.project()
.and("data.nick").as("nick")
.and("data.avatar").as("avatar")
.and("data.mailMd5").as("mailMd5")
.and("data.link").as("link")
.and("data.comment").as("comment")
.and("data.os").as("os")
.and("data.browser").as("browser")
.and("data.ipRegion").as("ipRegion")
.and("data.master").as("master")
.and("data.like").as("like")
.and("data.replies").as("replies")
.and("data.top").as("top")
.and("data.isSpam").as("isSpam")
.and("data.created").as("created")
);
AggregationResults<Comment> result = mongoTemplate.aggregate(aggregation, "result", Comment.class);
return result.getMappedResults();
}

/**
* 添加子评论
* 子评论存储在replies下
* @param rootId Result文档根Id
* @param commentId 父评论ID
* @param subComment 子评论内容
* @return UpdateResult
*/
public Object addSubComment(String rootId,String commentId,SubComment subComment){
Query query = Query.query(Criteria.where("_id").is(rootId));
query.addCriteria(Criteria.where("data._id").is(commentId));
Update update = new Update().push("data.$.replies", subComment);
UpdateResult result = mongoTemplate.updateFirst(query, update, Result.class);
return result;
}
}

测试

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
import org.bson.types.ObjectId;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import site.hikki.Application;
import site.hikki.pojo.cm.*;
import java.util.ArrayList;
import java.util.List;

@SpringBootTest(classes = Application.class)
class CommentDaoTest {

@Autowired
CommentDao commentDao;

//TODO: 该测试必须先运行 initResult 方法,自动创建 Comment 文档

@Test
public void initResult(){
Result result = commentDao.initResult();
System.out.println(result);
}


@Test
void addComment() {
Comment comment = new Comment();
comment.setId(new ObjectId().toString());
comment.setNick("小码同学");
comment.setAvatar("https://blog.hikki.site/avatar.png");
comment.setMailMd5("646c7393588d857cce5479df");
comment.setLink("https://blog.hikki.site");
comment.setComment("这博客好棒啊");
comment.setOs("Android");
comment.setBrowser("Firefox/113.0");
comment.setIpRegion("192.168.2.8");
comment.setMaster(true);
comment.setReplies(new ArrayList<>());
comment.setLike(5);
comment.setTop(1);
ArrayList<Comment> comments = new ArrayList<>();
comments.add(comment);
String rootId = "646d69a1dc3b9f57424e311b";
commentDao.addComment(rootId,comment);
}


/**
* 删除父评论
*
*/
@Test
void delComment() {
String rootId="646d69a1dc3b9f57424e311b";
String commentId="646d69ef9c64d41e198f7e6b";
Object o = commentDao.delComment(rootId, commentId);
System.out.println(o);
}

/**
* 修改data下的父评论的邮箱HAsh
*/
@Test
void updateComment() {
String rootId="646d69a1dc3b9f57424e311b"; // Result根ID
String mailMd5="646c7393588d857cce5479df"; // 邮件原Hash值
String newMailMd5="123456789"; // 需要更换后的邮件Hash值
Object o = commentDao.updateComment(rootId,mailMd5,newMailMd5);
System.out.println(o);
}

@Test
void findAllComment() {
List<Result> allComment = commentDao.findAllComment();
for (Result c : allComment) {
System.out.println(c);
}
}

@Test
void findCommentById() {
// 根据父评论ID查询
Object commentById = commentDao.findCommentById("646d78c4cac1fa60cf0d9e42");
System.out.println(commentById);
}

@Test
void addSubComment(){
SubComment comment = new SubComment(); // 子评论内容
comment.setId(new ObjectId().toString()); // 子评论自动生成ID
comment.setNick("小码同学");
comment.setAvatar("https://blog.hikki.site/avatar.png");
comment.setMailMd5("646c7393588d857cce5479df");
comment.setLink("https://blog.hikki.site");
comment.setComment("这博客好棒啊");
comment.setOs("Android");
comment.setBrowser("Firefox/113.0");
comment.setIpRegion("192.168.2.8");
comment.setLike(5);

String rootId="646d69a1dc3b9f57424e311b"; //Result文档根Id
String commentId="646d78c4cac1fa60cf0d9e42"; //父评论ID
Object o = commentDao.addSubComment(rootId,commentId,comment);
System.out.println(o);
}

}

总的来说,使用MongoDB来存储博客评论还是比较方便的,灵活的存储方式,如键值对、数组、嵌套对象,还支持全文搜索、聚合查询等高级功能。

主要也是MongoDB对于全文搜索的速度比较快,比MySQL快,如果在MySQL查询几十万或者几百万条数据中使用全文模糊查询,这速度就远不及MongoDB了。

Cache-SMS

前言

在实际应用中,我们有时候会用到邮箱验证,比如一个用户在使用邮箱注册时,我们需要使用一个邮箱给用户发送一个验证码邮件,并且这个验证码是有效期的,我们就可以使用缓存进行倒计时,下面我们使用Redis缓存技术进行一个简单的发送邮件验证码。

准备工作

添加依赖

我们需要用到Redis的依赖和Email依赖,打开pom.xml文件添加如下依赖并刷新一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
		<!--      Spring web项目-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis连接池-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<!-- email邮箱-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>

相关配置

我这里使用yml格式的配置方式,项目创建时默认是properties格式,你可以将后缀改为yml

1
2
3
4
5
6
7
8
9
spring:
mail:
host: smtp.163.com #
username: "aXXX56@163.com" #发件人
password: "SEFVOSNLWDQALK" #发件人密码(最好是使用授权码)
redis:
password:
host: 127.0.0.1
database: 2 # 指定Redis数据库

创建Server服务

创建发送邮件服务类,记得给类上添加@Service,同时,导入application.yml的username值,作为发送邮件的发件人。

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
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.stereotype.Service;
import javax.mail.internet.MimeMessage;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
public class MailService {

@Autowired
private JavaMailSender javaMailSender;

@Autowired
private RedisTemplate redisTemplate;

// 发件人(系统自带发件)
@Value("${spring.mail.username}")
private String fromUser;

//标题
private String subject = "小码博客身份验证码";
//正文
String nowTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());


/**
*
* @param emailCode 验证码
* @param toUser 收件人(游客)
*/
public void sendMail(String emailCode, String toUser) {
// 缓存验证码code
redisTemplate.opsForValue().set("code",emailCode);
// 设置验证码有效5分钟
redisTemplate.expire("code",5, TimeUnit.MINUTES);

System.out.println(fromUser);
System.out.println(toUser);
String context ="<div style='margin:10px auto 20px;border-radius:5px;background:#fff; box-shadow:0 0 10px #b1b1b1; text-align:left;'><div style='margin:0 40px; color:#999; border-bottom:1px dotted #DDD; padding:40px 0 30px; font-size:13px; text-align:center;'><a href='https://blog.hikki.site' style='text-decoration:none;display:inline-flex;' on_e-link-mark='yes'><img src='http://localhost:8080/BlogDemo_war_exploded/component/img/emailavatar.png' alt='小码博客' style='width:64px;height:64px;'><span style='line-height:24px; margin-left:15px; height:64px; overflow:hidden; text-align:left; max-width: 360px;'><span style='color:#555; font-size:20px; margin-top:12px; display:inline-block;'>小码博客</span><br><span style='color:#999'>——用户注册</span></span></a></div><div style='padding:30px 40px 40px; min-height:280px;'><p style='margin:0px; margin-bottom:20px;'><a data-auto-link='1' href='mailto:"+toUser+"'>"+toUser+"</a>,您好,</p><p style='margin:0px;'>您正在进行邮箱验证,本次请求的验证码如下,为了保障您帐号的安全性,请及时完成验证。</p><p style='margin:0px; text-align:center; margin-top:40px;'><span style='padding:8px 16px;border-radius:3px;text-align:center;text-decoration:none;background-color:#ecf4fb;color:#1890ff;font-size:18px; font-weight:700; letter-spacing:2px; margin:0;white-space:nowrap;display:inline-block;'>"+emailCode+"</span></p></div><div style='padding:20px 40px 40px;'><p>小码博客</p><p>"+nowTime+"</p></div></div>";

try {
MimeMessage message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message,true);
helper.setFrom(fromUser+"(邮箱验证)"); // 邮箱主题名
helper.setTo(toUser);
helper.setSubject(subject);
helper.setText(context,true);

//添加发送附件
// File f1 = new File("D:\\workspace\\springboot\\springboot_23_mail\\target\\springboot_23_mail-0.0.1-SNAPSHOT.jar");
// File f2 = new File("D:\\workspace\\springboot\\springboot_23_mail\\src\\main\\resources\\logo.png");
//
// helper.addAttachment(f1.getName(),f1);
// helper.addAttachment("这是图片.png",f2);

javaMailSender.send(message);
} catch (Exception e) {
e.printStackTrace();
}
}


public String getCode() {
return (String) redisTemplate.opsForValue().get("code");
}

public static void main(String[] args) {

}
}

创建控制器

使用一个控制来发送验证码,此处的code你可以自己随机生成一个,至于是使用字母+数字还是使用什么字符来发送,根据你的需求来做选择。

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
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import site.hikki.service.MailService;


@RestController
@RequestMapping
public class SendEmail {

@Autowired
private MailService send;
@GetMapping("/send")
public String send(){
String code ="46511";
send.sendMail(code,"1341151056@qq.com");
return "hello 小码同学!!!";
}

@GetMapping("/get")
public String getCOde(){
System.out.println(send.getCode());
return send.getCode();
}
}

本文章来源于我的博客:https://blog.hikki.site