spring-mail.properties(Spring的属性文件):
mail.host=smtp.163.com [email protected] mail.password=123456 #非SSL端口号 mail.port=25 #SSL端口号 #mail.port=465 mail.encoding=UTF-8 mail.smtp.auth=true mail.smtp.timeout=25000 [email protected];
spring-mail.xml(有关spring mail服务的Spring配置文件):
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:task="http://www.springframework.org/schema/task" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd"> <!-- 扫描该包下的Bean --> <context:component-scan base-package="com.wenniuwuren.mail.service" /> <!-- 邮件发送 --> <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host"> <value>${mail.host}</value> </property> <property name="username"> <value>${mail.username}</value> </property> <property name="password"> <value>${mail.password}</value> </property> <property name="port"> <value>${mail.port}</value> </property> <property name="defaultEncoding"> <value>${mail.encoding}</value> </property> <property name="protocol"> <value>smtp</value> </property> <property name="javaMailProperties"> <props> <prop key="mail.smtp.auth">${mail.smtp.auth}</prop> <!-- <prop key="mail.smtp.starttls.enable">true</prop> 取决于使用的邮件服务是否支持STARTTLS加密 --> <!-- <prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop> 取决于是否使用SSL端口发送邮件--> <prop key="mail.smtp.socketFactory.fallback">false</prop> <prop key="mail.smtp.timeout">${mail.smtp.timeout}</prop> <prop key="mail.sendTo">${mail.sendTo}</prop> <!-- <prop key="mail.debug">true</prop> 测试时开启--> </props> </property> </bean> <!-- 异步线程 --> <bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <property name="corePoolSize" value="10" /> <property name="maxPoolSize" value="30" /> </bean> </beans>配置完上面的, 记得在容器能扫描到的配置文件里加上:
<!-- 扫描邮件服务 --> <import resource="classpath:META-INF/spring-mail.xml"/>或者直接把上面的spring-mail.xml直接写在容器能扫描到的配置文件里, 但是会导致单个配置文件太长。
MailService接口:
public interface MailService { /** * 邮件发送统一入口 * @param email * */ public abstract void sendMail(Email email) throws Exception; /** * 异步发送 */ public abstract void sendMailByAsynchronousMode(final Email email) throws Exception; /** * 同步发送 */ public abstract void sendMailBySynchronizationMode(Email email) throws Exception; /** * 邮件发送 * @param subject * @param content */ public void sendMail(String subject, String content) throws Exception; }
import java.util.Properties; import javax.annotation.Resource; import javax.mail.internet.MimeMessage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.task.TaskExecutor; import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSenderImpl; import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.stereotype.Service; @Service("mailService") public class MailServiceImpl implements MailService{ @Resource JavaMailSender mailSender;// 注入 @Resource TaskExecutor taskExecutor; Logger logger = LoggerFactory.getLogger(MailServiceImpl.class); // 判断同步或者异步发送 @Override public void sendMail(Email email) throws Exception { JavaMailSenderImpl senderImpl = (JavaMailSenderImpl)mailSender; Properties mailProperties = senderImpl.getJavaMailProperties(); //获取接收人信息 String sendTo = mailProperties.getProperty("mail.sendTo"); email.setAddress(sendTo); if (email.getAddress() == null || email.getAddressArray().length == 0) { throw new ServiceException("缺少收件人地址"); } if (email.getAddressArray().length > 5) {// 收件地址大于5时,异步发送 sendMailByAsynchronousMode(email); } else { try { sendMailBySynchronizationMode(email); } catch (Exception e) { throw new Exception(e); } } } // 实现异步只需把同步发送放到Concurrent里面执行 @Override public void sendMailByAsynchronousMode(final Email email) throws Exception { taskExecutor.execute(new Runnable() { public void run() { try { sendMailBySynchronizationMode(email); } catch (Exception e) { throw new Exception(e); } } }); } // 同步发送邮件 @Override public void sendMailBySynchronizationMode(Email email) throws Exception { try{ JavaMailSenderImpl senderImpl = (JavaMailSenderImpl)mailSender; MimeMessage mime = mailSender.createMimeMessage(); MimeMessageHelper helper = new MimeMessageHelper(mime, false, "utf-8"); helper.setFrom(senderImpl.getUsername());// 发件人 helper.setTo(email.getAddressArray());// 收件人 if (email.getCc() != null && email.getCc().trim().length() > 0) { String cc[] = email.getCc().split(";"); helper.setCc(cc);// 抄送 } helper.setSubject(email.getSubject());// 邮件主题 helper.setText(email.getContent(), true);// true表示设定html格式 mailSender.send(mime); }catch(Exception e){ throw new Exception(e); } } // 发送邮件 入口方法 public void sendMail(String subject, String content) throws Exception { try { Email mail = new Email(); mail.setSubject(subject); mail.setContent(content); sendMail(mail); } catch (Exception e) { throw new Exception(e); } } }
Model层的JavaBean: Email
import java.io.Serializable; public class Email implements Serializable{ private static final long serialVersionUID = -1; /** 收件人 **/ private String address; /** 抄送给 **/ private String cc; /** 邮件主题 **/ private String subject; /** 邮件内容 **/ private String content; // ////////////////////////解析邮件地址////////////////////////////// public String[] getAddressArray() { if (this.address == null || this.address.trim().length()==0) { return null; } address = address.trim(); address.replaceAll(";", ";"); address.replaceAll(" ", ";"); address.replaceAll(",", ";"); address.replaceAll(",", ";"); address.replaceAll("|", ";"); return address.split(";"); } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getCc() { return cc; } public void setCc(String cc) { this.cc = cc; } public String getSubject() { return subject; } public void setSubject(String subject) { this.subject = subject; } public String getContent() { return content; } public void setContent(String content) { this.content = content; } }
@Resource private MailService mailService; // 注入 try{ // 业务逻辑 }catch(Exception e){ //发送邮件 mailService.sendMail("邮件主题", "邮件内容"); }
编码过程遇到的问题:
1. 错误:javax.mail.AuthenticationFailedException: 错误码(400 ~ 599)
这是最经常看到的, 原因可能有:
-- 用户名密码错误
-- 防火墙或杀毒软件屏蔽SMTP协议
-- 邮箱本身没有开启SMTP/POP协议支持(默认都是关闭的)
2. 错误:Could not send email: Could not convert socket to TLS
原因: 所使用的邮件服务器不支持通过STARTTLS keyword 的TLS加密
解决办法: 注释掉这个配置<prop key="mail.smtp.starttls.enable">true</prop>, 参照上文
3. 错误: javax.mail.MessagingException: Could not connect to SMTP host: smtp.163.com, port: 465, response: -1
原因:使用了SSL端口465但是却配置却没使用SSL的配置
解决办法: 配置<prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop>
参照上文, 把注释去掉就行了。
4. 其他服务器可以发送, 但是个别邮件服务器就算开启了SMTP协议支持也不行(如现在的QQ邮箱)
解决办法: 暂无(不知道广大博友怎么搞定)... 友情提示:先别试QQ邮箱, 试网易的136会省掉很多找bug的时间。
本文参考资料:
博客: 利用Spring框架封装的JavaMail实现同步或异步邮件发送
Wikipedia: STARTTLS
SVNForum: Thread: Could not send email: Could not convert socket to TLS