# 需求
系统添加一个用户之后,给这个用户发邮件,告知用户名密码等。为了不阻塞主线程,发邮件使用一个子线程来执行。
# 实现
<!-- 配置 freeMarker 视图解析器 --> | |
<bean id="viewResolverFtl" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver"> | |
<property name="viewClass" value="org.springframework.web.servlet.view.freemarker.FreeMarkerView"/> | |
<property name="contentType" value="text/html; charset=utf-8"/> | |
<property name="cache" value="false" /> | |
<property name="suffix" value=".ftl" /> | |
<property name="order" value="0"/> | |
<property name="requestContextAttribute" value="request"/> | |
</bean> | |
<!-- 配置 freeMarker 的模板路径 --> | |
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"> | |
<property name="templateLoaderPath" value="/WEB-INF/view/ftl/" /> | |
<property name="defaultEncoding" value="UTF-8" /> | |
<property name="freemarkerSettings"> | |
<props> | |
<prop key="template_update_delay">0</prop> | |
</props> | |
</property> | |
</bean> | |
<!--MailSender--> | |
<bean id="sender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> | |
<property name="host"> | |
<value>smtp.263.net</value> | |
</property> | |
<property name="username"> | |
<value>xxx@sss</value> | |
</property> | |
<property name="password"> | |
<value>xxx</value> | |
</property> | |
<property name="protocol"><value>smtp</value></property> | |
<property name="defaultEncoding"><value>utf-8</value></property> | |
<property name="javaMailProperties"> | |
<props> | |
<prop key="mail.smtp.auth">true</prop> | |
<prop key="mail.smtp.timeout">10000</prop> | |
<prop key="mail.smtp.from">xxx@sss</prop> | |
<prop key="mail.debug">true</prop> | |
<prop key="mail.smtp.socketFactory.class">javax.net.ssl.SSLSocketFactory</prop> | |
<prop key="mail.smtp.socketFactory.port">465</prop> | |
<prop key="mail.smtp.port">465</prop> | |
<prop key="mail.smtp.host">smtp.263.net</prop> | |
</props> | |
</property> | |
</bean> |
Spring 线程池配置:
<beans xmlns="http://www.springframework.org/schema/beans" | |
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | |
xsi:schemaLocation="http://www.springframework.org/schema/beans | |
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> | |
<bean id="threadPool" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> | |
<!-- 线程池维护线程的最少数量 --> | |
<property name="corePoolSize" value="5"></property> | |
<!-- 允许的空闲时间 --> | |
<property name="keepAliveSeconds" value="200"></property> | |
<!-- 线程池维护线程的最大数量 --> | |
<property name="maxPoolSize" value="10"></property> | |
<!-- 缓存队列 --> | |
<property name="queueCapacity" value="20"></property> | |
<!-- 对拒绝 task 的处理策略 --> | |
<property name="rejectedExecutionHandler"> | |
<bean class="java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy"></bean> | |
</property> | |
</bean> | |
</beans> |
邮件发送线程类:
import org.springframework.beans.factory.annotation.Autowired; | |
import org.springframework.mail.javamail.JavaMailSender; | |
import org.springframework.stereotype.Service; | |
import javax.mail.internet.MimeMessage; | |
@Service | |
public class MailThreadServiceImpl { | |
@Autowired | |
private JavaMailSender javaMailSender; | |
public Runnable execute(MimeMessage mimeMessage) { | |
return new SendMail(mimeMessage); | |
} | |
public class SendMail implements Runnable { | |
private MimeMessage mimeMessage; | |
public SendMail(MimeMessage mimeMessage) { | |
this.mimeMessage = mimeMessage; | |
} | |
@Override | |
public void run() { | |
if (mimeMessage != null) { | |
MailThreadServiceImpl.this.javaMailSender.send(mimeMessage); | |
System.out.println("===============邮件发送成功================="); | |
} | |
} | |
} | |
} |
业务逻辑方法:
public void sendEmail(User user) { | |
MimeMessage message = null; | |
try { | |
Map<String, String> data = new HashMap<>(); | |
data.put("logoUrl", ReadPropertiesUtil.readMailLogo()); | |
data.put("loginName", user.getLoginName()); | |
data.put("password", password); | |
Template template = freeMarkerConfigurer.getConfiguration().getTemplate("mail.ftl"); | |
// 解析模板并替换动态数据,最终 content 将替换模板文件中的 ${content} 标签。 | |
String htmlText = FreeMarkerTemplateUtils.processTemplateIntoString(template, data); | |
message = mailSender.createMimeMessage(); | |
MimeMessageHelper helper = new MimeMessageHelper(message, false, "utf8"); | |
helper.setFrom(mailSender.getUsername()); | |
helper.setTo(user.getEmail()); | |
helper.setSubject("注册成功-邮件"); | |
helper.setText(htmlText, true); | |
threadPoolTaskExecutor.execute(mailThreadService.execute(message)); | |
} catch (MessagingException e) { | |
System.out.println("=========new MimeMessageHelper(message, false, \"utf8\")===========创建模板邮件失败"); | |
} catch (IOException e) { | |
System.out.println("=========freeMarkerConfigurer.getConfiguration().getTemplate(\"mail.ftl\")==========配置模板失败"); | |
} catch (TemplateException e) { | |
System.out.println("=========FreeMarkerTemplateUtils.processTemplateIntoString(template, data);========模板渲染动态数据异常"); | |
} catch (Exception e) { | |
System.out.println("========================发送邮件失败=========================="); | |
} | |
} |
邮件模板:
mail.ftl
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> | |
<title>Insert title here</title> | |
<style> | |
body,h1,h2,h3,h4,h5,h6,p,ul,ol,dl,dd,input,button,table,tr,td { | |
margin: 0; | |
padding: 0; | |
outline: none; | |
} | |
h1,h2,h3,h4,h5,h6,b,i,em { | |
font-size: 16px; | |
font-weight: normal; | |
font-style: normal; | |
} | |
a { | |
color: #fff; | |
text-decoration: none; | |
} | |
ul,ol { | |
list-style: none; | |
} | |
input { | |
outline: none; | |
} | |
input,img,select { | |
vertical-align: top; | |
} | |
.last-bottom { | |
border-bottom: none !important; | |
} | |
.last-right { | |
/*border-right: none !important;*/ | |
margin-right: 0 !important; | |
} | |
.fl { | |
float: left; | |
} | |
.fr { | |
float: right; | |
} | |
/* 清除浮动 */ | |
.clearfix:after { | |
content: ""; | |
display: block; | |
clear: both; | |
} | |
.clearfix { | |
zoom: 1; | |
} | |
html { | |
font-size: 8.888888886666667vw; | |
} | |
body { | |
background: #f9f9f9; | |
} | |
.box { | |
width: 484px; | |
border-radius: 5px; | |
margin: 60px auto; | |
} | |
.top { | |
height:42px; | |
background: ; | |
/*background:#313D4B url(D:/freemarker/logo.png) no-repeat center;*/ | |
background:#313D4B url(${logoUrl}) no-repeat center; | |
background-size:100px 42px; | |
} | |
.content { | |
height:192px; | |
background: #fff; | |
overflow: hidden; | |
} | |
.word { | |
font-size: 12px; | |
color: #7c7c7c; | |
margin-top: 23px; | |
margin-left: 23px; | |
} | |
.center { | |
width: 230px; | |
margin: 30px auto; | |
font-size: 12px; | |
color: #7c7c7c; | |
} | |
.pass{ | |
margin-top: 11px; | |
} | |
.name { | |
color: #000; | |
} | |
.add { | |
margin-top: 11px; | |
} | |
.res { | |
color:#4098ca; | |
} | |
.bottom { | |
width: 260px; | |
margin: 30px auto; | |
font-size: 12px; | |
color: #c7c7c7; | |
} | |
.first { | |
text-align: center; | |
} | |
.sec { | |
width: 260px; | |
text-align: center; | |
} | |
.reach { | |
color: #4297cd; | |
} | |
.third { | |
text-align: center; | |
} | |
</style> | |
</head> | |
<body> | |
<div class="box"> | |
<div class="top"></div> | |
<div class="content"> | |
<div class="word">欢迎使用系统,您的账户已经成功开通. </div> | |
<div class="center"> | |
<div class="user"> | |
<span class="username">用户名:</span> | |
<span class="name">${loginName}</span> | |
</div> | |
<div class="pass"> | |
<span class="password">密码:</span> | |
<span class="name">${password}</span> | |
</div> | |
<div class="add"> | |
<span class="address">系统访问地址:</span> | |
<span class="res"> | |
<a href="http://xxx" style="color: blue"> | |
http://xxx</a> | |
</span> | |
</div> | |
</div> | |
</div> | |
<div class="bottom"> | |
<p class="first">如果您在使用系统时遇到任何问题可发邮件至</p> | |
<p class="sec"><span class="reach">xxx@xxx</span>获取帮助信息.</p> | |
<p class="third">本邮件由系统自动发出,请不要回复此邮件.</p> | |
</div> | |
</div> | |
</body> | |
</html> |
# 问题
在本地和测试服务器上都可以成功发送邮件,只有在线上服务器发送不成功。
# 原因
是由于线上服务器无法解析域名地址,需要在代码中将邮件服务器地址改成 IP 的地址,将 smtp.263.net 改为 211.150.65.66。