邮件发送功能(springBoot之自定义启动器)手把手教你制作邮箱

目录

一、介绍stater

        1、stater的理念:

        2、stater的实现:

        3、stater的原理图:

二、 java操作邮件(邮件的使用)

         步骤:

                     1、导入依赖

                     2、重写yml资源文件

                     3、写好测试类

                     4、进行测试

三、邮箱的制作(自定义启动器)

步骤:

1、先导入依赖

2、创建yml文件

获取授权码链接:什么是授权码,它又是如何设置?_QQ邮箱帮助中心

3、创建实体类资源文件

4、定义一个发送接口,并且实现接口

5、进行测试

四、使用邮箱

        1、提高邮箱使用灵活性

                 步骤:

                          1、导入yml文件注入依赖

                          2、在resource文件下添加文件

        2、将整个项目打包成jar包

        3、到之前的项目中导入邮箱依赖(必须使用同一个本地仓库)

        4、在测试类中加入注解

        5、进行测试:运行成功

五、结合redis实现操作

          1、步骤

                     1、导入操作redis的依赖

                     2、在applicaion.yml资源文件中加入redis资源

                     3、将帮助类写入到项目中

                     4、新建一个实体类和控制类

​​          2、优化邮箱使用

                    解决方法:

                    步骤:


一、介绍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、进行测试

很明显和之前的运行结果进行比较得出,使用异步操作可以提高程序运行速度,对于此上,真正的验证码还没有发送。

今天的知识就分享到这了,希望能够帮助到大家,制作不易,点赞则是对我最大的鼓励。

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值