目录
获取授权码链接:什么是授权码,它又是如何设置?_QQ邮箱帮助中心
2、在applicaion.yml资源文件中加入redis资源
一、介绍stater
1、stater的理念:
starter会把所有用到的依赖都给包含进来,避免了开发者自己去引入依赖所带来的麻烦。 需要注意的是不同的starter是为了解决不同的依赖,所以它们内部的实现可能会有很大的差异,例如jpa的starter和Redis的starter可能实现就不一样,这是因为starter的本质在于synthesize, 这是一层在逻辑层面的抽象,也许这种理念有点类似于Docker,因为它们都是在做一个“包装”的操作。
2、stater的实现:
虽然不同的starter实现起来各有差异, 但是他们基本上都会使用到两个相同的内容:ConfigurationProperties和AutoConfiguration。 因为SpringBoot坚信“约定大于配置”这一理念,所以我们使用ConfigurationProperties来保存我们的配置, 并且这些配置都可以有一个默认值,即在我们没有主动覆写原始配置的情况下,默认值就会生效,这在很多情况下是非常有用的。除此之外,starter的ConfigurationProperties还使得所有的配置属性被聚集到一个文件中 (一般在resources目录下的application.properties),这样我们就告别了Spring项目中XML地狱。
3、stater的原理图:
注意:其中最关键的是配置文件,用户可以自定义配置文件将自己的邮箱地址写入,以便提高邮箱灵活性
二、 java操作邮件(邮件的使用)
步骤:
1、导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2、重写yml资源文件
spring: application: name: springBoot_06 mail: host: smtp.qq.com username: 自己qq@qq.com password: exgdllmxqrhrieae properties: mail: smtp: auth: true starttls: enable: true required: true
3、写好测试类
package com.zj.code;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSender;
@SpringBootTest
class SpringBoot06ApplicationTests {
@Autowired
JavaMailSender mailSender;
@Test
void contextLoads() {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom("1551506470@qq.com");
message.setTo("1551506470@qq.com");
message.setSubject("主题:简单邮件");
message.setText("测试邮件内容");
mailSender.send(message);
}
}
注意:在测试类中特别要注意的地方就是要将以下注解打上
@Autowired JavaMailSender mailSender;
4、进行测试
测试成功
三、邮箱的制作(自定义启动器)
步骤:
1、先导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
2、创建yml文件
注意:在yml文件中四个属性的意思分别是
email:
enable: true --是否开启邮箱验证
host: smtp.qq.com --主机
username:自己qq@qq.com --账号(发送方)
password: uqoimlnpljuqihdd --密码(注意,此处这里是授权码,而不是密码)
获取授权码链接:什么是授权码,它又是如何设置?_QQ邮箱帮助中心
3、创建实体类资源文件
EmailProperties:
package com.yzm.yzmspringbootstarter;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* @author T440s
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
@ConfigurationProperties(prefix= "email")
public class EmailProperties {
private String host;
private String username;
private String password;
private String enable;
}
关于四个注解的说明:
@NoArgsConstructor:无参构造
@AllArgsConstructor:有参构造
@Data:get和set方法
@ConfigurationProperties(prefix= "email")作用:根据属性完成yml注入(需要bean对象被spring容器管理)
4、定义一个发送接口,并且实现接口
接口:EmailSender:
package com.yzm.yzmspringbootstarter;
public interface EmailSender {
String sendText(String receiver);
}
接口实现类:
EmailSenderImpl:
package com.yzm.yzmspringbootstarter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.mail.SimpleMailMessage;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import java.util.Properties;
@Configuration
@Slf4j
@EnableConfigurationProperties(value = EmailProperties.class)
@ConditionalOnProperty(prefix = "email", value = "enable", havingValue = "true")
public class EmailSenderImpl implements EmailSender {
private final EmailProperties emailProperties;
private final JavaMailSenderImpl mailSender;
@Autowired
public EmailSenderImpl(EmailProperties emailProperties) {
this.emailProperties = emailProperties;
mailSender = new JavaMailSenderImpl();
mailSender.setHost(emailProperties.getHost());
mailSender.setUsername(emailProperties.getUsername());
mailSender.setPassword(emailProperties.getPassword());
mailSender.setDefaultEncoding("Utf-8");
Properties p = new Properties();
p.setProperty("mail.smtp.auth", "true");
p.setProperty("mail.smtp.starttl .enable", "true");
p.setProperty("mail.smtp.starttls.required", "true");
mailSender.setJavaMailProperties(p);
}
@Override//此方法是发送验证码,并且是要显示接受方
public String sendText(String receiver) {
SimpleMailMessage message = new SimpleMailMessage();
message.setFrom(emailProperties.getUsername());
message.setTo(receiver);
message.setSubject("网站验证码");
message.setText("aabb");
mailSender.send(message);
return "aabb";
}
}
关于此类四个注解的说明:
@Configuration:将此类声明为组件,并且交给spring进行管理
@Slf4j:日志输出
@EnableConfigurationProperties(value = EmailProperties.class)完成对应元素组件化
@ConditionalOnProperty(prefix = "email", value = "enable", havingValue = "true")控制邮件功能是否可用
5、进行测试
测试类:
package com.yzm.yzmspringbootstarter;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class YzmSpringBootStarterApplicationTests {
@Autowired
private EmailSender sender;
@Test
void contextLoads() {
sender.sendText("1551506470@qq.com");
}
}
测试成功
注意:在制作邮箱的过程中需要注意两个问题:
1、在yml文件中的email中的格式要规范(中间要空一格)
()()email: enable: true host: smtp.qq.com username: 自己qq@qq.com password: uqoimlnpljuqihdd
2、账号以及收件方的都是写qq号码+qq.com,而不是创建邮箱时的账号
username: 自己qq@qq.com
四、使用邮箱
1、提高邮箱使用灵活性
由于每个人的邮箱都不一样,不可能将发送和收件人的邮箱以及是否启用邮箱都固定,所以要将yml中的内容进行更改,提高邮箱使用灵活性。
步骤:
1、导入yml文件注入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.1</version>
<configuration><classifier>exec</classifier></configuration>
</plugin>
2、在resource文件下添加文件
META-INF > spring.factories
文件内容:
org.springframework.boot.autoconfigure.
EnableAutoConfiguration=com.yzm.yzmspringbootstarter.EmailSenderImpl
此段代码的说明:当整个项目运行时,对应类就会随着运行
查看是否添加文件成功:
2、将整个项目打包成jar包
最后到本地仓库中查看有没有yzm这个文件夹(这个名字就是自己命名的)可以在pom文件中查看
<modelVersion>4.0.0</modelVersion> <groupId>com.yzm</groupId> <artifactId>yzm-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> <name>yzm-spring-boot-starter</name> <description>yzm-spring-boot-starter</description>
打包成功。
3、到之前的项目中导入邮箱依赖(必须使用同一个本地仓库)
<dependency> <groupId>com.yzm</groupId> <artifactId>yzm-spring-boot-starter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
4、在测试类中加入注解
@Autowired private EmailSender emailSender;
导入此注解时会进行报错,原因:没有配置资源文件
spring: application: name: springBoot_06 email: host: smtp.qq.com username: 自己qq@qq.com password: exgdllmxqrhrieae
加了基本配置文件后,还是报错(原因:没有开启邮箱,有提示说明导入依赖成功)
导入成功
5、进行测试:运行成功
五、结合redis实现操作
由于现在数据繁多,不可能每次都会进入到数据库拿取数据,这时候就会运用缓存来提高程序运行速度。
1、步骤
1、导入操作redis的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2、在applicaion.yml资源文件中加入redis资源
redis:
database: 0 #数据库索引
host: 127.0.0.1 #主机位置
port: 6379 #端口
password: #密码
jedis:
pool:
max-active: 8 #最大连接数
max-wait: -1 #最大阻塞等待时间(负数表示没限制)
max-idle: 8 #最大空闲
min-idle: 0 #最小空闲
timeout: 10000 #连接超时时间
3、将帮助类写入到项目中
conf--->CrossConfiguration
package com.zj.code.conf;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
/**
* @author 银喾
*/
@Configuration
public class CrossConfiguration extends WebMvcConfigurerAdapter {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry
/*可以跨域的路径*/
.addMapping("/**")
/*可以跨域的ip*/
.allowedOrigins("*")
/*可以跨域的方法*/
.allowedMethods("*")
/*设置预检请求多就失效*/
.maxAge(6000)
/*允许携带的头部*/
.allowedHeaders("*");
}
}
conf--->RedisConfiguration
package com.zj.code.conf;
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.context.annotation.Primary;
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.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.ClassUtils;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.time.Duration;
/**
* @author 银喾
*/
@Configuration
@EnableCaching
public class RedisConfiguration extends CachingConfigurerSupport {
@Bean
@Primary
CacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
.computePrefixWith(cacheName -> cacheName + ":-cache-:")
/*设置缓存过期时间*/
.entryTtl(Duration.ofHours(1))
/*禁用缓存空值,不缓存null校验*/
.disableCachingNullValues()
/*设置CacheManager的值序列化方式为json序列化,可使用加入@Class属性*/
.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(
new GenericJackson2JsonRedisSerializer()
));
/*使用RedisCacheConfiguration创建RedisCacheManager*/
RedisCacheManager manager = RedisCacheManager.builder(factory)
.cacheDefaults(cacheConfiguration)
.build();
return manager;
}
@Bean
@Primary
public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
redisTemplate.setConnectionFactory(factory);
RedisSerializer stringSerializer = new StringRedisSerializer();
/* key序列化 */
redisTemplate.setKeySerializer(stringSerializer);
/* value序列化 */
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
/* Hash key序列化 */
redisTemplate.setHashKeySerializer(stringSerializer);
/* Hash value序列化 */
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
@Bean
@Primary
@Override
public KeyGenerator keyGenerator() {
return (Object target, Method method, Object... params) -> {
final int NO_PARAM_KEY = 0;
final int NULL_PARAM_KEY = 53;
StringBuilder key = new StringBuilder();
/* Class.Method: */
key.append(target.getClass().getSimpleName())
.append(".")
.append(method.getName())
.append(":");
if (params.length == 0) {
return key.append(NO_PARAM_KEY).toString();
}
int count = 0;
for (Object param : params) {
/* 参数之间用,进行分隔 */
if (0 != count) {
key.append(',');
}
if (param == null) {
key.append(NULL_PARAM_KEY);
} else if (ClassUtils.isPrimitiveArray(param.getClass())) {
int length = Array.getLength(param);
for (int i = 0; i < length; i++) {
key.append(Array.get(param, i));
key.append(',');
}
} else if (ClassUtils.isPrimitiveOrWrapper(param.getClass()) || param instanceof String) {
key.append(param);
} else {
/*JavaBean一定要重写hashCode和equals*/
key.append(param.hashCode());
}
count++;
}
return key.toString();
};
}
}
util-->RedisUtil(由于内容过长,这个类在上一篇博客中有,建议到上篇博客摘要)
https://blog.csdn.net/m0_53151031/article/details/122831095?spm=1001.2014.3001.5501
4、新建一个实体类和控制类
pojo:
package com.zj.code.pojo;
import lombok.Data;
@Data
public class User {
private String account;
}
UserController:
package com.zj.code.controller;
import com.yzm.yzmspringbootstarter.EmailSender;
import com.zj.code.pojo.User;
import com.zj.code.util.RedisUtil;
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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/user")
public class UserController {
private EmailSender sender;
@GetMapping("/register")
public String register(User user) {
if (user.getAccount().length() < 5) {
return "不符合规则";
}
String yzm = RedisUtil.StringOps.get(user.getAccount());
if (yzm == null) {
yzm = sender.sendText(user.getAccount());
RedisUtil.StringOps.setEx(user.getAccount(), yzm, 1, TimeUnit.MINUTES);
return "验证码已发送";
}
return "验证码还是有效期";
}
/**
* 判断当前验证码与缓存中的验证码一致
* @param user 用户
* @param yzm 当前验证码
* @return 当前验证码与该用户在缓存中的验证是否是一致的
*/
@GetMapping("/verify")
public String verify(User user,String yzm) {
String code = RedisUtil.StringOps.get(user.getAccount());
if(code==null){
return "验证码无效";
}
return code.equals(yzm)?"yes":"no";
}
}
运行之后出现一个错误:
原因是没有打上自动注入注解
运行成功
2、优化邮箱使用
前言:在我们注册一个邮箱时,往往会出现注册缓慢,理想状态应该为先出现注册成功,在之后出现邮箱发送成功
解决方法:
1、消息队列
2、异步操作
通常采取第二种方法,使用异步操作
步骤:
1、创建一个task类,专用来发送验证码
package com.zj.code.util;
import com.yzm.yzmspringbootstarter.EmailSender;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
@Component
public class Task {
@Autowired
private EmailSender sender;
@Async
public void sendYzm(String account){
String yzm = sender.sendText(account);
RedisUtil.StringOps.setEx(account, yzm, 1, TimeUnit.MINUTES);
}
}
注意点:
一定要注入注解,不然会报错
其中
@Component(将此类声明为bean对象交给spring进行管理)
@Autowired(将其他类注入进来)
@Async(异步注解)这三个注解尤为重要
2、在启动类中注入开启异步注解
@EnableAsync
3、在controller类中注入task注解,
@Autowired private Task task;
package com.zj.code.controller;
import com.yzm.yzmspringbootstarter.EmailSender;
import com.zj.code.pojo.User;
import com.zj.code.util.RedisUtil;
import com.zj.code.util.Task;
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.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private Task task;
@GetMapping("/register")
public String register(User user) {
if (user.getAccount().length() < 5) {
return "不符合规则";
}
String yzm = RedisUtil.StringOps.get(user.getAccount());
if (yzm == null) {
task.sendYzm(user.getAccount());
return "验证码已发送";
}
return "验证码还是有效期";
}
/**
* 判断当前验证码与缓存中的验证码一致
* @param user 用户
* @param yzm 当前验证码
* @return 当前验证码与该用户在缓存中的验证是否是一致的
*/
@GetMapping("/verify")
public String verify(User user,String yzm) {
String code = RedisUtil.StringOps.get(user.getAccount());
if(code==null){
return "验证码无效";
}
return code.equals(yzm)?"yes":"no";
}
}
4、进行测试
很明显和之前的运行结果进行比较得出,使用异步操作可以提高程序运行速度,对于此上,真正的验证码还没有发送。
今天的知识就分享到这了,希望能够帮助到大家,制作不易,点赞则是对我最大的鼓励。