您的位置:首页 > 编程语言 > Java开发

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

2017-11-16 13:30 555 查看
同理消费者监听类一样可以做发短信,微信等等。。。

看这篇文章首先确保你已经阅读过我上一篇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自己处理方便的多了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息