发邮件,项目的必备功能之一,如果一个稍微模块化一点的公司,一般会单独出来一个项目专用来做公司的发送信息的功能,当然这个发送信息中不止包含发邮件,还会有短信、APP push等。这篇聊聊推送邮件。
在以前的开发中,公司用Java mail的比较多,由自己来写邮件的组装和发送功能,但是Java mail使用操作比较繁杂,后来渐渐的都开始使用spring提供的JavaMailSender工具来实现,用过的都知道,这叫一个爽,执行几个set、add操作,一个复杂的邮件就可以发送出去,但是在业务代码中发送邮件的位置很多,不能在每个业务逻辑位置都写一遍同样的set操作,这是基本常识,因此都会基于JavaMailSender再做一个工具类,这个工具类让调用者只用一句话实现就OK。引出主题工具类,接下来就开干吧。
这边的主要内容是写JavaMailSender,但是开始还是来看看JavaMail的基本实现,因为JavaMailSender也是基于JavaMail做的再次封装。
发送邮件的三个重点:
Session中实际也提供了获取transport的方法,但是这个transport不好用,还是习惯用Transport提供的静态调用。
/*Transport静态方法直接调用,将message传入即可*/ Transport.send(message); /*Session获取transport方式调用*/ Transport transport = session.getTransport(); //address需要手动添加,表示发送人 transport.sendMessage(message,new Address[1]);
上面Session的transport只能指定发送人,抄送人、密送人都不能添加,如果在MimeMessage中添加不知道能不能起作用,这个没有尝试过,如果你喜欢用这种方式可以分享一下。
Java mail有两个依赖,分别是mail和activation。
<dependency> <groupId>javax.mail</groupId> <artifactId>mail</artifactId> <version>1.4.7</version> </dependency> <dependency> <groupId>javax.activation</groupId> <artifactId>activation</artifactId> <version>1.1.1</version> </dependency>
简单的发送邮件没有复杂的逻辑,代码也很清晰,直接上代码:
private static void send() throws MessagingException { //构建Session Properties properties = new Properties(); properties.setProperty("mail.host","smtp.163.com"); properties.setProperty("mail.transport.protocol","smtp"); properties.setProperty("mail.smtp.auth","true"); properties.setProperty("mail.host.port","25"); Session session = Session.getDefaultInstance(properties, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("xxx@163.com","xxxxxx"); } }); session.setDebug(true); //构建邮件MimeMessage MimeMessage message = new MimeMessage(session); message.setSubject("这是一封测试邮件!"); message.setText("测试邮件内容随意"); message.setRecipient(MimeMessage.RecipientType.TO,new InternetAddress("itcrud@aliyun.com")); message.setFrom(new InternetAddress("xxx@163.com")); //发送邮件 Transport.send(message); }
需要注意的点:
com.sun.mail.smtp.SMTPSendFailedException: 553 Mail from must equal authorized user
;setRecipient
方法,在第一参数中指定抄送类型即可。复杂邮件使用的相对会比较多一点,在发送邮件的时候多少会带上内嵌图片或者附件。MimeMessage是整个邮件本体,构建复杂邮件的过程就是向这个本体中加不同的内容,Java mail提供了一个类MimeBodyPart
,表示邮件内容的组件。将需要发送的内容用组件包装起来,再塞到邮件本地中即可。
示例代码:
private static void send() throws MessagingException { //构建Session Properties properties = new Properties(); properties.setProperty("mail.host","smtp.163.com"); properties.setProperty("mail.transport.protocol","smtp"); properties.setProperty("mail.smtp.auth","true"); properties.setProperty("mail.host.port","25"); Session session = Session.getDefaultInstance(properties, new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { return new PasswordAuthentication("xxx@163.com","xxxxx"); } }); session.setDebug(true);//打印发送日志 //构建基础信息 MimeMessage message = new MimeMessage(session); message.setSubject("这是一封测试邮件!"); //message.setText("测试邮件内容随意"); message.setRecipient(MimeMessage.RecipientType.TO,new InternetAddress("itcrud@aliyun.com")); message.setFrom(new InternetAddress("xxx@163.com")); //构建内容 MimeBodyPart text = new MimeBodyPart(); text.setContent("<html><body><h3>你好,这是一封模板邮件!</h3><img src='cid:avatar'><h3>上面内嵌了一张图片↑↑↑↑</h3></body></html>", "text/html;charset=UTF-8"); MimeBodyPart bodyPart = new MimeBodyPart(); //构建内嵌图片 DataHandler dataHandler = new DataHandler(new FileDataSource("/Users/joker/Downloads/avatar.png")); bodyPart.setDataHandler(dataHandler); bodyPart.setContentID("avatar");//如果没有这句,表示作为附件发送,加上表示显示在邮件内容的一部分 //构建附件 MimeBodyPart attachment = new MimeBodyPart(); DataHandler attachmentHandler = new DataHandler(new FileDataSource("/Users/joker/Downloads/avatar.png")); attachment.setDataHandler(attachmentHandler); attachment.addHeader("Content-Type","UTF-8");//防止中文名乱码 attachment.setFileName("avatar.png");//这里名称记得带上后缀名,否则邮件附件下载下来需要手动添加后缀,可能会损坏 //构建邮件体内容 MimeMultipart mimeMultipart = new MimeMultipart(); mimeMultipart.addBodyPart(text); mimeMultipart.addBodyPart(bodyPart); mimeMultipart.addBodyPart(attachment); mimeMultipart.setSubType("mixed");//设置为mixed,别忘了哦 message.setContent(mimeMultipart); //发送邮件 Transport.send(message); }
复杂邮件构建的过程不是很难,就是代码量会多一点。
JavaMailSender是由spring提供的,功能很强大,对Java mail做了一层封装,在Spring boot中可以拿来用,是自动集成的。
和Spring boot整合后,引入依赖就很简单啦。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> <!--是在使用邮件模板的时候使用,不使用模板可以忽略--> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.6.4</version> </dependency>
使用JavaMailSender需要在配置文件中配置相关的信息,在Java mail中是通过Session来配置,在JavaMailSender上,在Spring项目中可以通过在配置文件中配置。(如果使用的是spring boot,在properties中配置,如果是Spring MVC项目就要用xml文件啦,或者自己手动的使用Configuration类来配置,本文以Spring boot为基础)
spring.mail.default-encoding=utf-8 spring.mail.host=smtp.163.com spring.mail.protocol=smtp spring.mail.properties.mail.smtp.auth=true spring.mail.properties.mail.transport.protocol=smtp spring.mail.properties.mail.smtp.starttls.enable=true spring.mail.properties.mail.smtp.starttls.required=true spring.mail.username=xxx@163.com spring.mail.password=xxxx
简单邮件发送方法,到底简单到什么程度,看下面的代码:
@RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes = ItcrudCommonsUtilApplication.class) public class JavaMailSenderTest { @Autowired private JavaMailSender javaMailSender; @Autowired private VelocityEngine velocityEngine;//暂时用不上,在下面说到模板邮件使用 @Test public void sendTest() throws MessagingException { //创建邮件本体 MimeMessage message = javaMailSender.createMimeMessage(); //创建本体编辑的协助者(YY出来的名字) MimeMessageHelper helper = new MimeMessageHelper(message); //构建邮件本体 helper.setSubject("邮件标题"); helper.setText("发送的内容"); helper.setTo("itcrud@aliyun.com"); helper.setFrom("xxx@163.com"); //发送邮件 javaMailSender.send(helper.getMimeMessage()); } }
看了这段发送邮件的代码,然后和上面Java mail的代码对比,果断的舍弃Java mail的有木有。
对于Java mail来说是复杂邮件,但是对于JavaMailSender来说,复杂也会变得简单。
示例代码:
@Test public void sendComplexText() throws MessagingException { MimeMessage message = javaMailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); //构建邮件本体内容 helper.setSubject("复杂邮件"); helper.setFrom("xxx@163.com"); helper.setTo("itcrud@aliyun.com"); helper.setText("<html><body><h3>你好,这是一封模板邮件!</h3><img src='cid:avatar'><h3>上面内嵌了一张图片↑↑↑↑</h3></body></html>", true); helper.addInline("avatar", new FileDataSource("/Users/joker/Downloads/avatar.png")); helper.addAttachment("avatar.png",new FileDataSource("/Users/joker/Downloads/avatar.png")); //发送邮件 javaMailSender.send(helper.getMimeMessage()); }
代码的减少量可以看得出来吧。那些复杂的封装逻辑就交给Spring来处理啦。
需要注意的两点:(刚开始用的时候被坑过)
上面的两个内容都不是最主要的,在将Java mail的时候最后一个功能很难说,代码量太多。下面我们用JavaMailSender来实现。
场景:在公司对外发送邮件的时候,除了附件、内嵌图片和普通文本,还有两个很重的东西,那就是签名和占位填充。个人发邮件的时候都会带上自己的签名,何况是公司向外发邮件啦。另外一个邮件的基本格式相同,只是部分填充内容不同,总不能每次发送文件都要重新写一个html文件吧。
但是想想,邮件签名就直接加在内容里面不就可以啦,然后使用html格式来进行邮件的排版。对的没有错,但是如果整个邮件的排版比较复杂,html内容很长怎么办。那也没问题啊,可以放在文件里面,通过流来读取文件不就可以了吗。对的的确是这样的,但是如果使用Java mail就要去手动的读取问题,不觉得会很麻烦吗。还有就是占位符的填充。
这个时候JavaMailSender帮我们解决了这一系列的问题。准确的说是和另一个插件Velocity结合来解决。这就是在引入依赖的时候,我加上velocity的依赖的重点所在。
只需要两步就可以搞定:
<!--模板文件的内容--> <html> <body> <h1>这是邮件的标题</h1> <div> <p>很长很长的邮件内容上半部分</p> <p>很长很长的邮件内容上半部分</p> </div> <img src="cid:avatar"> <div> <p>很长很长的邮件内容下半部分</p> <p>很长很长的邮件内容下半部分</p> <p>还有一部分内容看附件</p> </div> <div> <p> 这里是签名的内容 公司名称:xxxx有限责任公司 联系电话:xxxxxxxx </p> </div> </body> </html>
@Test public void sendSuperTest() throws MessagingException { MimeMessage message = javaMailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(message, true); //构建邮件本体内容 helper.setSubject("复杂邮件"); helper.setFrom("xxx@163.com"); helper.setTo("itcrud@aliyun.com"); //使用 VelocityContext context = new VelocityContext(); context.put("content","占位符需要填充的内容"); try (StringWriter writer = new StringWriter()) { velocityEngine.mergeTemplate("/mail/temp.vm", "UTF-8", context, writer); helper.setText(writer.toString(), true); } catch (Exception e) { e.printStackTrace(); } helper.addInline("avatar", new FileDataSource("/Users/joker/Downloads/avatar.png")); helper.addAttachment("avatar.png",new FileDataSource("/Users/joker/Downloads/avatar.png")); //发送邮件 javaMailSender.send(helper.getMimeMessage()); }
完美解决上面的两个问题,并且整个实现过程的代码量也是很少的,比上面Java mail发送一个复杂邮件还要少。
这里讲到了Java mail发送邮件和Spring封装后的JavaMailSender,两者对比来说,JavaMailSender要比Java mail直接实现要简单的多。如果你有时间其实可以自己实现一套Spring的这种封装,但是为何要重复造轮子呢。
码云(gitee):https://gitee.com/itcrud/itcrud-commons(mail)