利用activeMQ消息中间件整合spring mail发邮件

同理消费者监听类一样可以做发短信,微信等等。。。
看这篇文章首先确保你已经阅读过我上一篇spring boot整合activeMQ,实现ptp和topic两者消息模式
配置文件新增了spring mail的相关配置:

spring.mail.username=xxxxxxxxx@163.com
spring.mail.password=xxxxxxxxx
spring.mail.host=smtp.163.com
spring.mail.port=25
spring.mail.properties.mail.smtp.auth=true
spring.mail.properties.mail.smtp.starttls.enable=true
spring.mail.properties.mail.smtp.starttls.required=true

ps:这里提一下,由于使用163邮箱发邮件需要去163邮箱里设置勾选POP3/SMTP服务,选中后会让你设置独立密码,区别于邮箱登陆密码,spring.mail.password这个配置hi的密码就是独立密码而不是邮箱登录密码,否则会报权限报错
pom文件比之前多加入两个:

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

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.29</version>
</dependency>

1.首先为了方便测试我们要先把Customer2里面的@JmsListener监听注解注视掉,这样保证我们只有一个消费者监听消息队列。
2.我们需要在项目里新加一个utils包放工具类,EmailUtil就在其中

@Component
public class EmailUtil {
    @Autowired
    private JavaMailSender mailSenderAutowired;

    private static JavaMailSender mailSender;

    @Value("${spring.mail.username}")
    private String usernameValue;

    private static String username;

    @PostConstruct//bean初始化之前调用
    public void init() {
        this.mailSender = mailSenderAutowired;
        this.username = usernameValue;
    }


    public static void sendEmail(String to, String subject, String text){
        SimpleMailMessage message = new SimpleMailMessage();
        message = null;
        message.setFrom(username);
        message.setTo(to);
        message.setSubject(subject);
        message.setText(text);
        mailSender.send(message);
    }

    public static void sendEmailWithFile(String to, String subject, String text, InputStreamSource file) throws MessagingException {
        MimeMessage mimeMessage = mailSender.createMimeMessage();
        MimeMessageHelper helper = new MimeMessageHelper(mimeMessage, true);
        helper.setFrom(username);
        helper.setTo(to);
        helper.setSubject(subject);
        helper.setText(text);
        helper.addAttachment("附件-1.png", file);
        mailSender.send(mimeMessage);
    }
}

这里说明一点:
由于是工具类,所以类里面的方法是静态方法,静态方法内部是不能使用类里非静态变量的,所以mailSender和username都定义为静态的,但问题来了,由于定义为静态,代码运行时是无法将配置信息注入到变量里(由于编译起就开辟内存空间,并没有赋值,所以运行期这两个变量是null),于是我们定义两个普通变量来注入配置信息,然后在通过这个注解@PostConstruct在bean初始化之前把普通变量的值交给静态变量,这样运行时就不会报NPE了。
先看一下发邮件方法的入口controller:

@RestController
@RequestMapping("/sendEmail")
public class SendEmailController {
    @Autowired
    private SendEmailService sendEmailService;

    @GetMapping("/sendEmail/{text}")
    public void advice(@PathVariable("text") String text){
        sendEmailService.advice(text);
    }

}

很简单就一个普通的controller层,再看看service层:
先贴出来平时常规业务逻辑嵌入发邮件功能,这种常规写法存在很多问题,比如:
如果发送邮件出现异常那么发邮件下面的业务逻辑无法正常执行,即使try/catch了,由于这部分代码是同步的,下面的业务代码也需要等待邮件发完才能执行,耦合度很高,高并发情况下还有可能出现阻塞

public interface SendEmailService {

    void sendEmail(String text);

    void sendEmailWithFile(String text) throws MessagingException;

    void advice(String text);
}
@Service
public class SendEmailServiceImpl implements SendEmailService{
    @Autowired
    private ProductService productService;


    @Override
    public void advice(String text) {
//        JSONObject jsonObject = new JSONObject();
//        jsonObject.put("key","email");
//        jsonObject.put("text",text);
//        this.productService.sendMessage(jsonObject.toJSONString());
        this.sendEmail(text);
        System.out.println("邮件发送成,邮件内容是:" + text);
        //其他业务逻辑...
    }

    @Override
    public void sendEmail(String text) {
        EmailUtil.sendEmail("xxxxxx@qq.com","测试邮件",text);
    }

    @Override
    public void sendEmailWithFile(String text) throws MessagingException {
        InputStreamSource file = new FileSystemResource(new File("F:\\pic\\huaji.jpg"));
        EmailUtil.sendEmailWithFile("xxxxxx@qq.com","测试邮件",text,file);
    }
}

再来看看利用消息队列中间件去发邮件:
好处是把业务逻辑和发邮件功能解耦,即使发邮件失败也不会影响下面的业务逻辑正常执行,由于使用了MQ发邮件和业务逻辑是异步的,下面的代码不必等待邮件发送完毕才执行。


@Service
public class SendEmailServiceImpl implements SendEmailService{
    @Autowired
    private ProductService productService;


    @Override
    public void advice(String text) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("key","email");
        jsonObject.put("text",text);
        this.productService.sendMessage(jsonObject.toJSONString());
//        this.sendEmail(text);
        System.out.println("邮件发送成,邮件内容是:" + text);
        //其他业务逻辑...
    }

    @Override
    public void sendEmail(String text) {
        EmailUtil.sendEmail("xxxxxx@qq.com","测试邮件",text);
    }

    @Override
    public void sendEmailWithFile(String text) throws MessagingException {
        InputStreamSource file = new FileSystemResource(new File("F:\\pic\\huaji.jpg"));
        EmailUtil.sendEmailWithFile("xxxxxx@qq.com","测试邮件",text,file);
    }
}

由于使用mq做的不仅仅是发邮件还有其他功能所以定义一个json对方用key做标识,看一下Customer消费者部分代码:

@Service
public class Consumer {
    @Autowired
    private SendEmailService sendEmailService;
    private static int index = 0;

    // 使用JmsListener配置消费者监听的队列,其中text是接收到的消息
    @JmsListener(destination = "zh-topic")
    public void receiveQueue(String text) throws MessagingException {
        System.out.println("重试次数:" + index);
        System.out.println("Consumer收到:"+text);
        this.index = ++index;
        JSONObject jsonObject = JSONObject.parseObject(text);
        if ("email".equals(jsonObject.getString("key"))){//发邮件
            this.sendEmail(jsonObject.getString("text"));
        }else{
            //其他业务...
        }

    }

    public void sendEmail(String text) throws MessagingException {
        this.sendEmailService.sendEmailWithFile(text);
    }

}

这个类里面定义了一个静态的index变量是为了 测试mq在消费消息失败时候的重试机制,我们可以在EmailUtil的发邮件方法里自定义一个异常让发邮件失败,我们观察控制台会发现,activeMQ在消息未消费情况下会默认重试6次,每次重试间隔为1s钟
还要说的是,spring 提供的mail好方便,配置配置通过SimpleMailMessage或MimeMessage就可以发邮件了,添加附件也很方便,比以往使用java mail自己处理方便的多了

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值