经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » Spring Boot » 查看文章
从spring boot发邮件聊到开发的友好性
来源:cnblogs  作者:wphmoon  时间:2019/10/10 9:01:09  对本文有异议

   前些天帮一个朋友做网站,全站都是静态页面,唯一需要用到后端开发的是他需要一个留言板。传统的留言板一般都是提交后保存到数据库,然后提供一个后台的留言列表给管理人员看,我嫌麻烦,就决定留言提交到后台直接发邮件出去,这样就不用开发后台页面了,他也不需要登录一个什么后台才能看留言,两全其美,岂不美哉。

1、最简版spring boot发邮件

spring boot发邮件还是挺简单的,首先把发邮件的start加到pom里面:

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-mail</artifactId>
  4. </dependency>

然后在application.properties里面配置好关于发邮件的参数

  1. spring.mail.host=smtp.163.com
  2. spring.mail.port=25
  3. spring.mail.username=yourmail@163.com
  4. spring.mail.password=yourpassword

其中spring.mail.host和spring.mail.username是一一对应的,哪里的邮箱就要用哪里的smtp服务器

然后我写了一个controller来接收留言板的内容:

  1. @Value("${spring.mail.username}")
  2. private String fromMail;
  3. @Autowired
  4. private JavaMailSender mailSender;
  5. @RequestMapping(value = "/getNote", method = RequestMethod.POST)
  6. public String getNote(Note note) {
  7. MimeMessage mimeMessage = mailSender.createMimeMessage();
  8. MimeMessageHelper helper;
  9. try {
  10. helper = new MimeMessageHelper(mimeMessage, true);
  11. //发件人
  12. helper.setFrom(fromMail,note.yourName);
  13. //收件人(留言内容最终发往的邮箱地址)
  14. helper.setTo("recieve@mail.com");
  15. //标题
  16. helper.setSubject(note.yourSubject);
  17. //文本
  18. helper.setText("from email:"+note.yourEmail+"\n"+note.yourMessage);
  19. mailSender.send(mimeMessage);
  20. } catch (MessagingException | UnsupportedEncodingException e) {
  21. // TODO Auto-generated catch block
  22. e.printStackTrace();
  23. }
  24. return "redirect:return.htm";
  25. }
  1. public class Note {
  2. String yourName;
  3. String yourEmail;
  4. String yourSubject;
  5. String yourMessage;
  6. //getter,setter省略
  7. }
  • note对象是留言板的内容
  • JavaMailSender和MimeMessageHelper是官方推荐的好基友,一般配套使用
  • fromMail就是在application.properties里面配置的spring.mail.username属性,也就是发邮件的一方,用helper.setTo(...)配置
  • 额外说一下,由于填留言板时一般也会留邮件地址,但那个邮件地址和这里的任何一个设置邮件的地方毫无关系,只需要在邮件的文本里面做记录就好了,我之前就弱智了一把,把留言板里面的邮件地址填到helper.setTo(...),结果浪费了我半个小时查这种bug,真是一言难尽...

2、授权码发邮件

     很多时候,按照上面的方法发邮件会出现下面的错误:

  1. javax.mail.AuthenticationFailedException: 535 Error: authentication failed

其中的一个原因是邮件服务器使用了授权码登录方式,也就是第三方登录不能直接使用账号密码,而需要使用一种授权码的方式,比如上面的163.com邮箱,就可以设置授权码登录,设置界面如下:

QQ邮箱更是默认就需要用授权码登录,QQ邮箱的授权登录操作文档请看这里

使用授权码登录后,需要把之前application.properties里面spring.mail.password的内容从密码换成授权码,就能够正常发邮件了。

3、简单聊下JavaMailSender和MimeMessageHelper

我们查下JavaMailSender的代码就知道,它其实背景比较复杂。首先它继承了org.springframework.mail.MailSender接口。

  1. public interface JavaMailSender extends MailSender {
  2. ...
  3. }

而它本身也是一个接口,实现类只有一个,JavaMailSenderImpl

我们再来翻JavaMailSenderImpl的代码,发现spring并没有自己来做发送邮件的功能,而是直接用了java自身的邮件发送功能,核心是这一段

  1. protected void doSend(MimeMessage[] mimeMessages, @Nullable Object[] originalMessages) throws MailException {
  2. Map<Object, Exception> failedMessages = new LinkedHashMap<>();
  3. Transport transport = null;
  4. try {
  5. for (int i = 0; i < mimeMessages.length; i++) {
  6. // Check transport connection first...
  7. if (transport == null || !transport.isConnected()) {
  8. if (transport != null) {
  9. try {
  10. transport.close();
  11. }
  12. catch (Exception ex) {
  13. // Ignore - we're reconnecting anyway
  14. }
  15. transport = null;
  16. }
  17. try {
  18. transport = connectTransport();
  19. }
  20. catch (AuthenticationFailedException ex) {
  21. throw new MailAuthenticationException(ex);
  22. }
  23. catch (Exception ex) {
  24. // Effectively, all remaining messages failed...
  25. for (int j = i; j < mimeMessages.length; j++) {
  26. Object original = (originalMessages != null ? originalMessages[j] : mimeMessages[j]);
  27. failedMessages.put(original, ex);
  28. }
  29. throw new MailSendException("Mail server connection failed", ex, failedMessages);
  30. }
  31. }
  32. // Send message via current transport...
  33. MimeMessage mimeMessage = mimeMessages[i];
  34. try {
  35. if (mimeMessage.getSentDate() == null) {
  36. mimeMessage.setSentDate(new Date());
  37. }
  38. String messageId = mimeMessage.getMessageID();
  39. mimeMessage.saveChanges();
  40. if (messageId != null) {
  41. // Preserve explicitly specified message id...
  42. mimeMessage.setHeader(HEADER_MESSAGE_ID, messageId);
  43. }
  44. Address[] addresses = mimeMessage.getAllRecipients();
  45. transport.sendMessage(mimeMessage, (addresses != null ? addresses : new Address[0]));
  46. }
  47. catch (Exception ex) {
  48. Object original = (originalMessages != null ? originalMessages[i] : mimeMessage);
  49. failedMessages.put(original, ex);
  50. }
  51. }
  52. }
  53. finally {
  54. try {
  55. if (transport != null) {
  56. transport.close();
  57. }
  58. }
  59. catch (Exception ex) {
  60. if (!failedMessages.isEmpty()) {
  61. throw new MailSendException("Failed to close server connection after message failures", ex,
  62. failedMessages);
  63. }
  64. else {
  65. throw new MailSendException("Failed to close server connection after message sending", ex);
  66. }
  67. }
  68. }
  69. if (!failedMessages.isEmpty()) {
  70. throw new MailSendException(failedMessages);
  71. }
  72. }

doSend方法中调用的核心类就是Transport类,这个类的包名是javax.mail。spring不愧是集成大师,java自带的mail功能经过spring的标准化包装就成了spring自身功能的一部分,再通过spring boot的包装,用starter的方式再次做简化,我们就能够直接通过极简的方式使用了。

当然,简化的方法多种多样,另外的一种形式的包装就是使用helper类的方法,spring使用的就是MimeMessageHelper。在javax.mail在处理邮件的方式上,使用的是分而治之的办法,不同的类处理不同的问题,所以看到很多的类在处理各种问题和情况。

这种做法在实现功能上是很好的,把一个复杂的问题分解成若干个小问题,分别实现。但对使用的开发人员就谈不上友好了,容易出现以下几个问题:

  • 不直观,调用者不知道从何下手
  • 查找麻烦,类太多而且功能分散,不容易找到对应的功能类
  • 关系复杂,经常对要引用哪个类会很没有把握,因为太多处理单一问题的类
  • 没有统一的入口,上手难,很难脱离文档直接使用

针对上述情况,spring通过MimeMessageHelper,把几乎所有邮件发送需要处理的问题就集中到了这个类里面,使用方便又好找。下面是这个类所有的方法。

这个类不可谓不复杂,基本上涵盖了所有发邮件方面的功能,但因为都集成在一个类里面,非常方便好用,可以说是一个对程序员友好的典范了,值得大家在开发时做借鉴。

 

原文链接:http://www.cnblogs.com/wphmoon/p/11644937.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号